diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 6500699..00a8aa8 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -48,7 +48,7 @@ jobs: run: | python -m pytest tests/ -v \ --timeout=30 \ - --cov=openspace \ + --cov=scion \ --cov-report=term-missing \ --cov-report=xml \ --cov-fail-under=20 @@ -75,7 +75,7 @@ jobs: run: pip install mypy==1.16.0 - name: Run mypy - run: mypy openspace/ --ignore-missing-imports + run: mypy scion/ --ignore-missing-imports dependency-audit: name: Dependency Audit (pip-audit) diff --git a/RUNBOOK.yaml b/RUNBOOK.yaml index f2a6771..5d2bb7b 100644 --- a/RUNBOOK.yaml +++ b/RUNBOOK.yaml @@ -100,8 +100,8 @@ epics: branch: "epic/3.1-skill-schema" pr: 12 review_round: 2 - source_file: "openspace/skill_engine/store.py" - target_module: "openspace/skill_engine/skill_schema.py" + source_file: "scion/skill_engine/store.py" + target_module: "scion/skill_engine/skill_schema.py" description: > Extract SkillSchema and SkillVersion frozen dataclasses/models from store.py. These are the core domain types for the skill persistence layer. @@ -117,8 +117,8 @@ epics: branch: "epic/3.2-skill-repository" pr: 15 review_round: 2 - source_file: "openspace/skill_engine/store.py" - target_module: "openspace/skill_engine/skill_repository.py" + source_file: "scion/skill_engine/store.py" + target_module: "scion/skill_engine/skill_repository.py" description: > Extract save_record, load_record, load_all, load_active, delete_record, deactivate/reactivate and all sync variants into a focused repository class. @@ -134,8 +134,8 @@ epics: branch: "epic/3.3-lineage-tracker" pr: 17 review_round: 2 - source_file: "openspace/skill_engine/store.py" - target_module: "openspace/skill_engine/lineage_tracker.py" + source_file: "scion/skill_engine/store.py" + target_module: "scion/skill_engine/lineage_tracker.py" description: > Extract get_ancestry, get_lineage_tree, _subtree, find_children, evolve_skill into a focused lineage/derivation graph module. @@ -150,8 +150,8 @@ epics: branch: "epic/3.4-analysis-store" pr: 19 review_round: 2 - source_file: "openspace/skill_engine/store.py" - target_module: "openspace/skill_engine/analysis_store.py" + source_file: "scion/skill_engine/store.py" + target_module: "scion/skill_engine/analysis_store.py" description: > Extract record_analysis, load_analyses, load_analyses_for_task, load_all_analyses, load_evolution_candidates, _insert_analysis @@ -167,8 +167,8 @@ epics: branch: "epic/3.5-tag-search" pr: 21 review_round: 2 - source_file: "openspace/skill_engine/store.py" - target_module: "openspace/skill_engine/tag_index_search.py" + source_file: "scion/skill_engine/store.py" + target_module: "scion/skill_engine/tag_index_search.py" description: > Extract find_skills_by_tool, load_by_category, get_summary, get_stats, get_top_skills, get_task_skill_summary, count into search/analytics module. @@ -183,8 +183,8 @@ epics: branch: "epic/3.6-migration-manager" pr: 23 review_round: 2 - source_file: "openspace/skill_engine/store.py" - target_module: "openspace/skill_engine/migration_manager.py" + source_file: "scion/skill_engine/store.py" + target_module: "scion/skill_engine/migration_manager.py" description: > Extract _init_db, _make_connection, _cleanup_wal_on_startup, _ensure_open, _db_retry, vacuum, clear into a DB lifecycle and schema migration module. @@ -202,7 +202,7 @@ epics: notes: > Additional PRs: #26 (E2E validation, 7 blockers fixed), #27 (warning cleanup), #28 (batch hydration + mypy fixes). All merged. 1,356 tests passing. - source_file: "openspace/skill_engine/store.py" + source_file: "scion/skill_engine/store.py" target_module: null description: > Final integration epic: verify all 7 modules work together, add backward @@ -222,8 +222,8 @@ epics: branch: null pr: null review_round: 0 - source_file: "openspace/tool_layer.py" - target_module: "openspace/tool_registry.py" + source_file: "scion/tool_layer.py" + target_module: "scion/tool_registry.py" description: > Extract tool registration, discovery, and backend listing from OpenSpace class. acceptance: @@ -237,8 +237,8 @@ epics: branch: null pr: null review_round: 0 - source_file: "openspace/tool_layer.py" - target_module: "openspace/skill_selector.py" + source_file: "scion/tool_layer.py" + target_module: "scion/skill_selector.py" description: > Extract _select_and_inject_skills, _get_skill_selection_llm into focused skill-first routing module. @@ -253,8 +253,8 @@ epics: branch: null pr: null review_round: 0 - source_file: "openspace/tool_layer.py" - target_module: "openspace/execution_engine.py" + source_file: "scion/tool_layer.py" + target_module: "scion/execution_engine.py" description: > Extract execute method and fallback orchestration logic. acceptance: @@ -268,8 +268,8 @@ epics: branch: null pr: null review_round: 0 - source_file: "openspace/tool_layer.py" - target_module: "openspace/recording_service.py" + source_file: "scion/tool_layer.py" + target_module: "scion/recording_service.py" description: > Extract _maybe_analyze_execution, _maybe_evolve_quality and all trace capture/recording logic. @@ -284,8 +284,8 @@ epics: branch: null pr: null review_round: 0 - source_file: "openspace/tool_layer.py" - target_module: "openspace/llm_adapter.py" + source_file: "scion/tool_layer.py" + target_module: "scion/llm_adapter.py" description: > Extract LLM client initialization and provider abstraction. acceptance: @@ -299,8 +299,8 @@ epics: branch: null pr: null review_round: 0 - source_file: "openspace/tool_layer.py" - target_module: "openspace/tool_layer_facade.py" + source_file: "scion/tool_layer.py" + target_module: "scion/tool_layer_facade.py" description: > Reduce tool_layer.py to thin facade that composes the 5 extracted modules. Preserve public API (OpenSpace class, OpenSpaceConfig). @@ -316,8 +316,8 @@ epics: branch: null pr: null review_round: 0 - source_file: "openspace/mcp_server.py" - target_module: "openspace/mcp/tool_handlers.py" + source_file: "scion/mcp_server.py" + target_module: "scion/mcp/tool_handlers.py" description: > Extract execute_task, search_skills, fix_skill, upload_skill handlers and their helpers into focused handler module. @@ -332,8 +332,8 @@ epics: branch: null pr: null review_round: 0 - source_file: "openspace/mcp_server.py" - target_module: "openspace/mcp/auth_middleware.py" + source_file: "scion/mcp_server.py" + target_module: "scion/mcp/auth_middleware.py" description: > Extract RateLimitMiddleware, BearerTokenMiddleware, and HMAC auth into dedicated auth middleware module. @@ -348,13 +348,13 @@ epics: branch: null pr: null review_round: 0 - source_file: "openspace/mcp_server.py" - target_module: "openspace/mcp/server.py" + source_file: "scion/mcp_server.py" + target_module: "scion/mcp/server.py" description: > Extract endpoint wiring (MCPRouter) and server lifecycle (run_mcp_server) - into clean server module. Create openspace/mcp/ package. + into clean server module. Create scion/mcp/ package. acceptance: - - "openspace/mcp/ package exists with clean separation" + - "scion/mcp/ package exists with clean separation" - "mcp_server.py is thin facade or deleted" - "All existing tests pass unchanged" @@ -383,8 +383,8 @@ epics: branch: null pr: null review_round: 0 - source_file: "openspace/skill_engine/evolver.py" - target_module: "openspace/skill_engine/evolution/models.py" + source_file: "scion/skill_engine/evolver.py" + target_module: "scion/skill_engine/evolution/models.py" description: > Extract EvolutionTrigger, EvolutionContext, _sanitize_skill_name into domain models module. @@ -399,8 +399,8 @@ epics: branch: null pr: null review_round: 0 - source_file: "openspace/skill_engine/evolver.py" - target_module: "openspace/skill_engine/evolution/orchestrator.py" + source_file: "scion/skill_engine/evolver.py" + target_module: "scion/skill_engine/evolution/orchestrator.py" description: > Extract evolve, _execute_contexts, schedule_background, _log_background_result into orchestration module. @@ -415,8 +415,8 @@ epics: branch: null pr: null review_round: 0 - source_file: "openspace/skill_engine/evolver.py" - target_module: "openspace/skill_engine/evolution/triggers/" + source_file: "scion/skill_engine/evolver.py" + target_module: "scion/skill_engine/evolution/triggers/" description: > Extract process_analysis, process_tool_degradation, process_metric_check into three focused trigger modules. @@ -431,8 +431,8 @@ epics: branch: null pr: null review_round: 0 - source_file: "openspace/skill_engine/evolver.py" - target_module: "openspace/skill_engine/evolution/confirmation.py" + source_file: "scion/skill_engine/evolver.py" + target_module: "scion/skill_engine/evolution/confirmation.py" description: > Extract _llm_confirm_evolution, _parse_confirmation into LLM confirmation/approval module. @@ -447,8 +447,8 @@ epics: branch: null pr: null review_round: 0 - source_file: "openspace/skill_engine/evolver.py" - target_module: "openspace/skill_engine/evolution/engines/" + source_file: "scion/skill_engine/evolver.py" + target_module: "scion/skill_engine/evolution/engines/" description: > Extract _evolve_fix, _evolve_derived, _evolve_captured, _run_evolution_loop, _parse_evolution_output, _apply_with_retry @@ -464,14 +464,14 @@ epics: branch: null pr: null review_round: 0 - source_file: "openspace/skill_engine/evolver.py" + source_file: "scion/skill_engine/evolver.py" target_module: null description: > Replace evolver.py with thin facade or delete. Verify all 9 evolution modules integrate correctly. acceptance: - "evolver.py is facade or deleted" - - "openspace/skill_engine/evolution/ package complete" + - "scion/skill_engine/evolution/ package complete" - "All existing tests pass" - id: "5.7" @@ -481,8 +481,8 @@ epics: branch: null pr: null review_round: 0 - source_file: "openspace/agents/grounding_agent.py" - target_module: "openspace/agents/grounding/" + source_file: "scion/agents/grounding_agent.py" + target_module: "scion/agents/grounding/" description: > Extract set_skill_context, clear_skill_context, _cap_message_content, _truncate_messages into focused modules. @@ -497,8 +497,8 @@ epics: branch: null pr: null review_round: 0 - source_file: "openspace/agents/grounding_agent.py" - target_module: "openspace/agents/grounding/" + source_file: "scion/agents/grounding_agent.py" + target_module: "scion/agents/grounding/" description: > Extract process (core loop), construct_messages, _default_system_prompt into execution loop and prompt builder modules. @@ -513,8 +513,8 @@ epics: branch: null pr: null review_round: 0 - source_file: "openspace/agents/grounding_agent.py" - target_module: "openspace/agents/grounding/" + source_file: "scion/agents/grounding_agent.py" + target_module: "scion/agents/grounding/" description: > Extract _get_available_tools, _load_all_tools, _visual_analysis_callback, _scan_workspace_files, _check_workspace_artifacts into focused modules. @@ -529,8 +529,8 @@ epics: branch: null pr: null review_round: 0 - source_file: "openspace/agents/grounding_agent.py" - target_module: "openspace/agents/grounding/result_telemetry.py" + source_file: "scion/agents/grounding_agent.py" + target_module: "scion/agents/grounding/result_telemetry.py" description: > Extract _build_final_result, _format_tool_executions, _generate_final_summary, _record_agent_execution into result formatting and telemetry module. @@ -545,14 +545,14 @@ epics: branch: null pr: null review_round: 0 - source_file: "openspace/agents/grounding_agent.py" + source_file: "scion/agents/grounding_agent.py" target_module: null description: > Replace grounding_agent.py with thin facade or delete. Verify all 8 grounding modules integrate correctly. Full /8eyes + /collab on P5. acceptance: - "grounding_agent.py is facade or deleted" - - "openspace/agents/grounding/ package complete" + - "scion/agents/grounding/ package complete" - "Full suite passes (1028+ tests)" - "/8eyes + /collab buyoff on complete P5" diff --git a/gdpval_bench/run_benchmark.py b/gdpval_bench/run_benchmark.py index 215ed38..51eb7cf 100644 --- a/gdpval_bench/run_benchmark.py +++ b/gdpval_bench/run_benchmark.py @@ -69,7 +69,7 @@ try: from dotenv import load_dotenv - _pkg_env = _OPENSPACE_ROOT / "openspace" / ".env" + _pkg_env = _OPENSPACE_ROOT / "scion" / ".env" if _pkg_env.is_file(): load_dotenv(_pkg_env) load_dotenv() # also try CWD/.env @@ -396,7 +396,7 @@ def _evaluate_task( def _make_config(cfg: Dict, phase: str, worker_id: int = 0): """Create a OpenSpaceConfig for one worker.""" - from openspace.tool_layer import OpenSpaceConfig + from scion.tool_layer import OpenSpaceConfig rd = _results_dir(cfg) # Each worker gets its own recording dir to avoid collisions @@ -592,7 +592,7 @@ async def _run_phase_serial( ) -> List[Dict[str, Any]]: """Run one phase sequentially. A single OpenSpace instance is reused so that skills accumulate within the phase.""" - from openspace.tool_layer import OpenSpace + from scion.tool_layer import OpenSpace rd = _results_dir(cfg) results_file = rd / f"{phase}_results.jsonl" @@ -660,7 +660,7 @@ async def _run_phase_concurrent( - Token tracking uses ContextVar so litellm callbacks route to the correct per-task bucket automatically. """ - from openspace.tool_layer import OpenSpace + from scion.tool_layer import OpenSpace rd = _results_dir(cfg) results_file = rd / f"{phase}_results.jsonl" @@ -1191,8 +1191,8 @@ def _stats(lst: list) -> dict: def _wipe_skill_db() -> None: - """Delete the shared .openspace/openspace.db for a fresh start.""" - db_file = _OPENSPACE_DB_DIR / "openspace.db" + """Delete the shared .openspace/scion.db for a fresh start.""" + db_file = _OPENSPACE_DB_DIR / "scion.db" for f in [db_file, db_file.with_suffix(".db-wal"), db_file.with_suffix(".db-shm")]: if f.exists(): f.unlink() @@ -1200,8 +1200,8 @@ def _wipe_skill_db() -> None: def _backup_skill_db(dest: Path) -> None: - """Copy the current .openspace/openspace.db to dest.""" - db_file = _OPENSPACE_DB_DIR / "openspace.db" + """Copy the current .openspace/scion.db to dest.""" + db_file = _OPENSPACE_DB_DIR / "scion.db" if db_file.exists(): dest.parent.mkdir(parents=True, exist_ok=True) shutil.copy2(str(db_file), str(dest)) @@ -1379,7 +1379,7 @@ async def main(args: argparse.Namespace) -> None: # Snapshot skills after Phase 1 try: - from openspace.skill_engine import SkillStore + from scion.skill_engine import SkillStore store = SkillStore() skills = _snapshot_skills(store) @@ -1462,7 +1462,7 @@ def _check_environment(cfg: Dict) -> bool: # 3. Check openspace try: - from openspace.tool_layer import OpenSpace + from scion.tool_layer import OpenSpace print(" ✅ openspace importable") except ImportError as e: diff --git a/openspace/agents/__init__.py b/openspace/agents/__init__.py deleted file mode 100644 index b18e87f..0000000 --- a/openspace/agents/__init__.py +++ /dev/null @@ -1,9 +0,0 @@ -from openspace.agents.base import AgentRegistry, AgentStatus, BaseAgent -from openspace.agents.grounding_agent import GroundingAgent - -__all__ = [ - "BaseAgent", - "AgentStatus", - "AgentRegistry", - "GroundingAgent", -] diff --git a/openspace/prompts/__init__.py b/openspace/prompts/__init__.py deleted file mode 100644 index a7cf399..0000000 --- a/openspace/prompts/__init__.py +++ /dev/null @@ -1,4 +0,0 @@ -from openspace.prompts.grounding_agent_prompts import GroundingAgentPrompts -from openspace.prompts.skill_engine_prompts import SkillEnginePrompts - -__all__ = ["GroundingAgentPrompts", "SkillEnginePrompts"] diff --git a/pyproject.toml b/pyproject.toml index 689e16b..9b6b77f 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -3,14 +3,15 @@ requires = ["setuptools>=68.0", "wheel"] build-backend = "setuptools.build_meta" [project] -name = "openspace" +name = "scion-skills" version = "0.1.0" -description = "OpenSpace" +description = "Scion — Self-evolving skills for AI coding agents" readme = "README.md" requires-python = ">=3.12" license = {text = "MIT"} authors = [ - {name = "OpenSpace Team@HKUDS"} + {name = "OpenSpace Team@HKUDS"}, + {name = "Brian Krafft"}, ] dependencies = [ @@ -60,20 +61,21 @@ dev = [ ] all = [ - "openspace[macos,linux,windows,dev]", + "scion-skills[macos,linux,windows,dev]", ] [project.urls] -Repository = "https://github.com/HKUDS/OpenSpace" -"Bug Tracker" = "https://github.com/HKUDS/OpenSpace/issues" +Repository = "https://github.com/Deepfreezechill/OpenSpace" +"Bug Tracker" = "https://github.com/Deepfreezechill/OpenSpace/issues" +"Upstream" = "https://github.com/HKUDS/OpenSpace" [project.scripts] -openspace = "openspace.__main__:run_main" -openspace-server = "openspace.local_server.main:main" -openspace-mcp = "openspace.mcp_server:run_mcp_server" -openspace-download-skill = "openspace.cloud.cli.download_skill:main" -openspace-upload-skill = "openspace.cloud.cli.upload_skill:main" -openspace-dashboard = "openspace.dashboard_server:main" +scion = "scion.__main__:run_main" +scion-server = "scion.local_server.main:main" +scion-mcp = "scion.mcp_server:run_mcp_server" +scion-download-skill = "scion.cloud.cli.download_skill:main" +scion-upload-skill = "scion.cloud.cli.upload_skill:main" +scion-dashboard = "scion.dashboard_server:main" [tool.ruff] target-version = "py313" @@ -101,10 +103,10 @@ ignore = [ "gdpval_bench/*" = ["E", "F", "W"] # benchmark scripts — upstream code [tool.setuptools] -packages = {find = {where = ["."], include = ["openspace*"]}} +packages = {find = {where = ["."], include = ["scion*"]}} [tool.setuptools.package-data] -openspace = [ +scion = [ "config/*.json", "config/*.json.example", "local_server/config.json", diff --git a/openspace/.env.example b/scion/.env.example similarity index 100% rename from openspace/.env.example rename to scion/.env.example diff --git a/openspace/__init__.py b/scion/__init__.py similarity index 58% rename from openspace/__init__.py rename to scion/__init__.py index 4868066..083f906 100644 --- a/openspace/__init__.py +++ b/scion/__init__.py @@ -4,11 +4,11 @@ from typing import Dict as _Dict if _TYPE_CHECKING: - from openspace.agents import GroundingAgent as GroundingAgent - from openspace.llm import LLMClient as LLMClient - from openspace.recording import RecordingManager as RecordingManager - from openspace.tool_layer import OpenSpace as OpenSpace - from openspace.tool_layer import OpenSpaceConfig as OpenSpaceConfig + from scion.agents import GroundingAgent as GroundingAgent + from scion.llm import LLMClient as LLMClient + from scion.recording import RecordingManager as RecordingManager + from scion.tool_layer import OpenSpace as OpenSpace + from scion.tool_layer import OpenSpaceConfig as OpenSpaceConfig __version__ = "0.1.0" @@ -33,18 +33,18 @@ # Map attribute → sub-module that provides it _attr_to_module: _Dict[str, str] = { # Main API - "OpenSpace": "openspace.tool_layer", - "OpenSpaceConfig": "openspace.tool_layer", + "OpenSpace": "scion.tool_layer", + "OpenSpaceConfig": "scion.tool_layer", # Core Components - "GroundingAgent": "openspace.agents", - "GroundingClient": "openspace.grounding.core.grounding_client", - "LLMClient": "openspace.llm", - "BaseTool": "openspace.grounding.core.tool.base", - "ToolResult": "openspace.grounding.core.types", - "BackendType": "openspace.grounding.core.types", + "GroundingAgent": "scion.agents", + "GroundingClient": "scion.grounding.core.grounding_client", + "LLMClient": "scion.llm", + "BaseTool": "scion.grounding.core.tool.base", + "ToolResult": "scion.grounding.core.types", + "BackendType": "scion.grounding.core.types", # Recording System - "RecordingManager": "openspace.recording", - "RecordingViewer": "openspace.recording.viewer", + "RecordingManager": "scion.recording", + "RecordingViewer": "scion.recording.viewer", } @@ -56,7 +56,7 @@ def __getattr__(name: str) -> _Any: corresponding functionality is explicitly used. """ if name not in _attr_to_module: - raise AttributeError(f"module 'openspace' has no attribute '{name}'") + raise AttributeError(f"module 'scion' has no attribute '{name}'") module_name = _attr_to_module[name] module = _imp(module_name) diff --git a/openspace/__main__.py b/scion/__main__.py similarity index 91% rename from openspace/__main__.py rename to scion/__main__.py index 4a961d6..8c0d686 100644 --- a/openspace/__main__.py +++ b/scion/__main__.py @@ -4,12 +4,12 @@ import sys from typing import Optional -from openspace.tool_layer import OpenSpace, OpenSpaceConfig -from openspace.utils.cli_display import CLIDisplay -from openspace.utils.display import colorize -from openspace.utils.logging import Logger -from openspace.utils.ui import OpenSpaceUI, create_ui -from openspace.utils.ui_integration import UIIntegration +from scion.tool_layer import OpenSpace, OpenSpaceConfig +from scion.utils.cli_display import CLIDisplay +from scion.utils.display import colorize +from scion.utils.logging import Logger +from scion.utils.ui import OpenSpaceUI, create_ui +from scion.utils.ui_integration import UIIntegration logger = Logger.get_logger(__name__) @@ -50,7 +50,7 @@ def print_summary(self, result: dict): CLIDisplay.print_result_summary(result) def _suppress_logs(self): - log_names = ["openspace", "openspace.grounding", "openspace.agents"] + log_names = ["scion", "scion.grounding", "scion.agents"] for name in log_names: log = logging.getLogger(name) self._original_log_levels[name] = log.level @@ -111,7 +111,7 @@ async def single_query_mode(openspace: OpenSpace, query: str, ui_manager: UIMana def _print_status(openspace: OpenSpace): """Print system status""" - from openspace.utils.display import Box, BoxStyle + from scion.utils.display import Box, BoxStyle box = Box(width=70, style=BoxStyle.ROUNDED, color="bl") print() @@ -119,12 +119,12 @@ def _print_status(openspace: OpenSpace): print(box.separator_line(indent=4)) status_lines = [ - f"Initialized: {colorize('Yes' if openspace.is_initialized() else 'No', 'g' if openspace.is_initialized() else 'rd')}", - f"Running: {colorize('Yes' if openspace.is_running() else 'No', 'y' if openspace.is_running() else 'g')}", - f"Model: {colorize(openspace.config.llm_model, 'c')}", + f"Initialized: {colorize('Yes' if scion.is_initialized() else 'No', 'g' if scion.is_initialized() else 'rd')}", + f"Running: {colorize('Yes' if scion.is_running() else 'No', 'y' if scion.is_running() else 'g')}", + f"Model: {colorize(scion.config.llm_model, 'c')}", ] - if openspace.is_initialized(): + if scion.is_initialized(): backends = openspace.list_backends() status_lines.append(f"Backends: {colorize(', '.join(backends), 'c')}") @@ -176,9 +176,9 @@ def _create_argument_parser() -> argparse.ArgumentParser: async def refresh_mcp_cache(config_path: Optional[str] = None): """Refresh MCP tool cache by starting servers one by one and saving tool metadata.""" - from openspace.config import get_config, load_config - from openspace.grounding.backends.mcp import MCPProvider, get_tool_cache - from openspace.grounding.core.types import BackendType, SessionConfig + from scion.config import get_config, load_config + from scion.grounding.backends.mcp import MCPProvider, get_tool_cache + from scion.grounding.core.types import BackendType, SessionConfig print("Refreshing MCP tool cache...") print("Servers will be started one by one (start -> get tools -> close).") @@ -375,15 +375,15 @@ async def _initialize_openspace(config: OpenSpaceConfig, args) -> OpenSpace: CLIDisplay.print_initialization_progress(init_steps, show_header=False) if not args.config: - original_log_level = Logger.get_logger("openspace").level - for log_name in ["openspace", "openspace.grounding", "openspace.agents"]: + original_log_level = Logger.get_logger("scion").level + for log_name in ["scion", "scion.grounding", "scion.agents"]: Logger.get_logger(log_name).setLevel(logging.WARNING) await openspace.initialize() # Restore log level if not args.config: - for log_name in ["openspace", "openspace.grounding", "openspace.agents"]: + for log_name in ["scion", "scion.grounding", "scion.agents"]: Logger.get_logger(log_name).setLevel(original_log_level) # Print initialization results @@ -428,8 +428,8 @@ async def main(): # Connect UI (if enabled) if ui_integration: - ui_integration.attach_llm_client(openspace._llm_client) - ui_integration.attach_grounding_client(openspace._grounding_client) + ui_integration.attach_llm_client(scion._llm_client) + ui_integration.attach_grounding_client(scion._grounding_client) CLIDisplay.print_system_ready() ui_manager = UIManager(ui, ui_integration) diff --git a/scion/agents/__init__.py b/scion/agents/__init__.py new file mode 100644 index 0000000..9259c2f --- /dev/null +++ b/scion/agents/__init__.py @@ -0,0 +1,9 @@ +from scion.agents.base import AgentRegistry, AgentStatus, BaseAgent +from scion.agents.grounding_agent import GroundingAgent + +__all__ = [ + "BaseAgent", + "AgentStatus", + "AgentRegistry", + "GroundingAgent", +] diff --git a/openspace/agents/base.py b/scion/agents/base.py similarity index 96% rename from openspace/agents/base.py rename to scion/agents/base.py index bd84b0f..5819652 100644 --- a/openspace/agents/base.py +++ b/scion/agents/base.py @@ -4,12 +4,12 @@ from abc import ABC, abstractmethod from typing import TYPE_CHECKING, Any, Dict, List, Optional, Type -from openspace.utils.logging import Logger +from scion.utils.logging import Logger if TYPE_CHECKING: - from openspace.grounding.core.grounding_client import GroundingClient - from openspace.llm import LLMClient - from openspace.recording import RecordingManager + from scion.grounding.core.grounding_client import GroundingClient + from scion.llm import LLMClient + from scion.recording import RecordingManager logger = Logger.get_logger(__name__) diff --git a/openspace/agents/grounding_agent.py b/scion/agents/grounding_agent.py similarity index 98% rename from openspace/agents/grounding_agent.py rename to scion/agents/grounding_agent.py index 6d6938c..e999ced 100644 --- a/openspace/agents/grounding_agent.py +++ b/scion/agents/grounding_agent.py @@ -4,17 +4,17 @@ import json from typing import TYPE_CHECKING, Any, Dict, List, Optional -from openspace.agents.base import BaseAgent -from openspace.grounding.core.types import BackendType, ToolResult -from openspace.platforms.screenshot import ScreenshotClient -from openspace.prompts import GroundingAgentPrompts -from openspace.utils.logging import Logger +from scion.agents.base import BaseAgent +from scion.grounding.core.types import BackendType, ToolResult +from scion.platforms.screenshot import ScreenshotClient +from scion.prompts import GroundingAgentPrompts +from scion.utils.logging import Logger if TYPE_CHECKING: - from openspace.grounding.core.grounding_client import GroundingClient - from openspace.llm import LLMClient - from openspace.recording import RecordingManager - from openspace.skill_engine import SkillRegistry + from scion.grounding.core.grounding_client import GroundingClient + from scion.llm import LLMClient + from scion.recording import RecordingManager + from scion.skill_engine import SkillRegistry logger = Logger.get_logger(__name__) @@ -256,7 +256,7 @@ async def process(self, context: Dict[str, Any]) -> Dict[str, Any]: # Record retrieved tools if self._recording_manager: - from openspace.recording import RecordingManager + from scion.recording import RecordingManager await RecordingManager.record_retrieved_tools( task_instruction=instruction, @@ -276,7 +276,7 @@ async def process(self, context: Dict[str, Any]) -> Dict[str, Any]: messages = self.construct_messages(context) # Record initial conversation setup once (system prompts + user instruction + tool definitions) - from openspace.recording import RecordingManager + from scion.recording import RecordingManager await RecordingManager.record_conversation_setup( setup_messages=copy.deepcopy(messages), @@ -576,7 +576,7 @@ async def _get_available_tools(self, task_description: Optional[str]) -> List: # Append retrieve_skill tool when skill registry is available if self._skill_registry and self._skill_registry.list_skills(): - from openspace.skill_engine.retrieve_tool import RetrieveSkillTool + from scion.skill_engine.retrieve_tool import RetrieveSkillTool retrieve_llm = self._tool_retrieval_llm or self._llm_client retrieve_tool = RetrieveSkillTool( diff --git a/openspace/app/__init__.py b/scion/app/__init__.py similarity index 77% rename from openspace/app/__init__.py rename to scion/app/__init__.py index c0276bb..9cef180 100644 --- a/openspace/app/__init__.py +++ b/scion/app/__init__.py @@ -1,7 +1,7 @@ """Application layer — composition root and service wiring. The :class:`AppContainer` holds references to all domain services, -wired through Protocol interfaces from :mod:`openspace.domain.ports`. +wired through Protocol interfaces from :mod:`scion.domain.ports`. No module in the codebase should construct services directly — they should receive them from the container. """ diff --git a/openspace/app/container.py b/scion/app/container.py similarity index 99% rename from openspace/app/container.py rename to scion/app/container.py index 067708b..8bfcafd 100644 --- a/openspace/app/container.py +++ b/scion/app/container.py @@ -22,7 +22,7 @@ from dataclasses import dataclass, field from typing import Any, List, Optional -from openspace.domain.ports import ( +from scion.domain.ports import ( AgentExecutorPort, AnalysisPort, AuthPort, diff --git a/openspace/app/factory.py b/scion/app/factory.py similarity index 96% rename from openspace/app/factory.py rename to scion/app/factory.py index 288d0c5..2f18aa8 100644 --- a/openspace/app/factory.py +++ b/scion/app/factory.py @@ -2,14 +2,14 @@ Production:: - from openspace.app.factory import build_container + from scion.app.factory import build_container container = await build_container(config) await container.startup() Testing:: - from openspace.app.factory import build_test_container + from scion.app.factory import build_test_container container = build_test_container() assert container.llm is not None # pre-wired mock @@ -19,8 +19,8 @@ from typing import Any, Optional -from openspace.app.container import AppContainer -from openspace.domain.ports import ( +from scion.app.container import AppContainer +from scion.domain.ports import ( AgentExecutorPort, AnalysisPort, AuthPort, @@ -62,7 +62,7 @@ async def build_container( Currently accepts explicit service overrides. In Phase 4, this will read ``config`` to auto-construct concrete implementations - from :mod:`openspace.tool_layer.OpenSpaceConfig`. + from :mod:`scion.tool_layer.OpenSpaceConfig`. Parameters ---------- diff --git a/openspace/auth/__init__.py b/scion/auth/__init__.py similarity index 100% rename from openspace/auth/__init__.py rename to scion/auth/__init__.py diff --git a/openspace/auth/bearer.py b/scion/auth/bearer.py similarity index 98% rename from openspace/auth/bearer.py rename to scion/auth/bearer.py index 1b34dd2..9b48536 100644 --- a/openspace/auth/bearer.py +++ b/scion/auth/bearer.py @@ -22,7 +22,7 @@ import os from typing import Any, Callable -logger = logging.getLogger("openspace.auth") +logger = logging.getLogger("scion.auth") BEARER_TOKEN_ENV = "OPENSPACE_MCP_BEARER_TOKEN" MIN_TOKEN_LENGTH = 32 diff --git a/openspace/auth/provider.py b/scion/auth/provider.py similarity index 99% rename from openspace/auth/provider.py rename to scion/auth/provider.py index 25918fe..bdb88b6 100644 --- a/openspace/auth/provider.py +++ b/scion/auth/provider.py @@ -44,7 +44,7 @@ from dataclasses import dataclass, field from enum import Enum -from openspace.sandbox.leases import TrustTier +from scion.sandbox.leases import TrustTier # --------------------------------------------------------------------------- # #104 — Token Claims Model @@ -480,7 +480,7 @@ def authorize_lease( import logging as _logging -_registry_logger = _logging.getLogger("openspace.auth.registry") +_registry_logger = _logging.getLogger("scion.auth.registry") class RegistryFullError(AuthError): diff --git a/openspace/auth/rate_limit.py b/scion/auth/rate_limit.py similarity index 99% rename from openspace/auth/rate_limit.py rename to scion/auth/rate_limit.py index 6f91213..f7de2a9 100644 --- a/openspace/auth/rate_limit.py +++ b/scion/auth/rate_limit.py @@ -33,7 +33,7 @@ from collections import defaultdict from typing import Any, Callable -logger = logging.getLogger("openspace.auth") +logger = logging.getLogger("scion.auth") # Environment variable names RATE_LIMIT_PER_TOKEN_ENV = "OPENSPACE_RATE_LIMIT_PER_TOKEN" diff --git a/openspace/cloud/__init__.py b/scion/cloud/__init__.py similarity index 74% rename from openspace/cloud/__init__.py rename to scion/cloud/__init__.py index 5efdfaa..4723198 100644 --- a/openspace/cloud/__init__.py +++ b/scion/cloud/__init__.py @@ -7,20 +7,20 @@ - ``generate_embedding`` — OpenAI embedding generation """ -from openspace.cloud.auth import get_openspace_auth +from scion.cloud.auth import get_openspace_auth def __getattr__(name: str): if name == "OpenSpaceClient": - from openspace.cloud.client import OpenSpaceClient + from scion.cloud.client import OpenSpaceClient return OpenSpaceClient if name == "SkillSearchEngine": - from openspace.cloud.search import SkillSearchEngine + from scion.cloud.search import SkillSearchEngine return SkillSearchEngine if name == "generate_embedding": - from openspace.cloud.embedding import generate_embedding + from scion.cloud.embedding import generate_embedding return generate_embedding raise AttributeError(f"module {__name__!r} has no attribute {name!r}") diff --git a/openspace/cloud/auth.py b/scion/cloud/auth.py similarity index 93% rename from openspace/cloud/auth.py rename to scion/cloud/auth.py index 75afba6..78def34 100644 --- a/openspace/cloud/auth.py +++ b/scion/cloud/auth.py @@ -16,7 +16,7 @@ import os from typing import Dict, Optional -logger = logging.getLogger("openspace.cloud") +logger = logging.getLogger("scion.cloud") OPENSPACE_DEFAULT_BASE = "https://open-space.cloud/api/v1" @@ -29,7 +29,7 @@ def get_openspace_auth() -> tuple[Dict[str, str], str]: and the API base URL. If no credentials are found, ``auth_headers`` is empty. """ - from openspace.host_detection import read_host_mcp_env + from scion.host_detection import read_host_mcp_env auth_headers: Dict[str, str] = {} api_base = OPENSPACE_DEFAULT_BASE @@ -65,7 +65,7 @@ def get_api_base(cli_override: Optional[str] = None) -> str: Priority: ``cli_override`` → env var → host agent config → default. """ - from openspace.host_detection import read_host_mcp_env + from scion.host_detection import read_host_mcp_env if cli_override: return cli_override.rstrip("/") @@ -83,7 +83,7 @@ def get_auth_headers_or_exit() -> Dict[str, str]: """Resolve auth headers for CLI scripts. Exits on failure.""" import sys - from openspace.host_detection import read_host_mcp_env + from scion.host_detection import read_host_mcp_env env_key = os.environ.get("OPENSPACE_API_KEY", "").strip() if env_key: diff --git a/openspace/cloud/cli/__init__.py b/scion/cloud/cli/__init__.py similarity index 100% rename from openspace/cloud/cli/__init__.py rename to scion/cloud/cli/__init__.py diff --git a/openspace/cloud/cli/download_skill.py b/scion/cloud/cli/download_skill.py similarity index 93% rename from openspace/cloud/cli/download_skill.py rename to scion/cloud/cli/download_skill.py index 2f116f5..8b9ecfd 100644 --- a/openspace/cloud/cli/download_skill.py +++ b/scion/cloud/cli/download_skill.py @@ -12,8 +12,8 @@ import sys from pathlib import Path -from openspace.cloud.auth import get_api_base, get_auth_headers_or_exit -from openspace.cloud.client import CloudError, OpenSpaceClient +from scion.cloud.auth import get_api_base, get_auth_headers_or_exit +from scion.cloud.client import CloudError, OpenSpaceClient def main() -> None: diff --git a/openspace/cloud/cli/upload_skill.py b/scion/cloud/cli/upload_skill.py similarity index 95% rename from openspace/cloud/cli/upload_skill.py rename to scion/cloud/cli/upload_skill.py index d4156c6..04cc273 100644 --- a/openspace/cloud/cli/upload_skill.py +++ b/scion/cloud/cli/upload_skill.py @@ -13,8 +13,8 @@ import sys from pathlib import Path -from openspace.cloud.auth import get_api_base, get_auth_headers_or_exit -from openspace.cloud.client import CloudError, OpenSpaceClient +from scion.cloud.auth import get_api_base, get_auth_headers_or_exit +from scion.cloud.client import CloudError, OpenSpaceClient def main() -> None: diff --git a/openspace/cloud/client.py b/scion/cloud/client.py similarity index 99% rename from openspace/cloud/client.py rename to scion/cloud/client.py index 47d752b..f124ba6 100644 --- a/openspace/cloud/client.py +++ b/scion/cloud/client.py @@ -25,7 +25,7 @@ from pathlib import Path from typing import Any, Dict, List, Optional -logger = logging.getLogger("openspace.cloud") +logger = logging.getLogger("scion.cloud") SKILL_FILENAME = "SKILL.md" SKILL_ID_FILENAME = ".skill_id" @@ -253,7 +253,7 @@ def upload_skill( Returns a result dict with status, record_id, etc. """ - from openspace.skill_engine.skill_utils import parse_frontmatter + from scion.skill_engine.skill_utils import parse_frontmatter skill_path = Path(skill_dir) skill_file = skill_path / SKILL_FILENAME diff --git a/openspace/cloud/embedding.py b/scion/cloud/embedding.py similarity index 97% rename from openspace/cloud/embedding.py rename to scion/cloud/embedding.py index b48c557..df11304 100644 --- a/openspace/cloud/embedding.py +++ b/scion/cloud/embedding.py @@ -9,7 +9,7 @@ import urllib.request from typing import List, Optional, Tuple -logger = logging.getLogger("openspace.cloud") +logger = logging.getLogger("scion.cloud") # Constants (duplicated here to avoid top-level import of skill_ranker) SKILL_EMBEDDING_MODEL = "openai/text-embedding-3-small" @@ -41,7 +41,7 @@ def resolve_embedding_api() -> Tuple[Optional[str], str]: return oa_key, base try: - from openspace.host_detection import get_openai_api_key + from scion.host_detection import get_openai_api_key host_key = get_openai_api_key() if host_key: diff --git a/openspace/cloud/search.py b/scion/cloud/search.py similarity index 94% rename from openspace/cloud/search.py rename to scion/cloud/search.py index 2001e28..076b876 100644 --- a/openspace/cloud/search.py +++ b/scion/cloud/search.py @@ -17,18 +17,18 @@ import re from typing import Any, Dict, List, Optional -logger = logging.getLogger("openspace.cloud") +logger = logging.getLogger("scion.cloud") def _check_safety(text: str) -> list[str]: """Lazy wrapper — avoids importing skill_engine at module load time.""" - from openspace.skill_engine.skill_utils import check_skill_safety + from scion.skill_engine.skill_utils import check_skill_safety return check_skill_safety(text) def _is_safe(flags: list[str]) -> bool: - from openspace.skill_engine.skill_utils import is_skill_safe + from scion.skill_engine.skill_utils import is_skill_safe return is_skill_safe(flags) @@ -123,7 +123,7 @@ def _bm25_phase( limit: int, ) -> List[Dict[str, Any]]: """BM25 rough-rank to keep top candidates for embedding stage.""" - from openspace.skill_engine.skill_ranker import SkillCandidate, SkillRanker + from scion.skill_engine.skill_ranker import SkillCandidate, SkillRanker ranker = SkillRanker(enable_cache=True) bm25_candidates = [ @@ -151,7 +151,7 @@ def _score_phase( query_embedding: Optional[List[float]], ) -> List[Dict[str, Any]]: """Compute hybrid score = vector_score + lexical_boost.""" - from openspace.cloud.embedding import cosine_similarity + from scion.cloud.embedding import cosine_similarity scored = [] for c in candidates: @@ -218,7 +218,7 @@ def build_local_candidates( Returns: List of candidate dicts ready for ``SkillSearchEngine.search()``. """ - from openspace.cloud.embedding import build_skill_embedding_text + from scion.cloud.embedding import build_skill_embedding_text candidates: List[Dict[str, Any]] = [] for s in skills: @@ -336,7 +336,7 @@ async def hybrid_search_skills( Returns: Ranked result dicts (same format as ``SkillSearchEngine.search()``). """ - from openspace.cloud.embedding import generate_embedding + from scion.cloud.embedding import generate_embedding q = query.strip() if not q: @@ -349,14 +349,14 @@ async def hybrid_search_skills( if source in ("all", "cloud"): try: - from openspace.cloud.auth import get_openspace_auth - from openspace.cloud.client import OpenSpaceClient + from scion.cloud.auth import get_openspace_auth + from scion.cloud.client import OpenSpaceClient auth_headers, api_base = get_openspace_auth() if auth_headers: client = OpenSpaceClient(auth_headers, api_base) try: - from openspace.cloud.embedding import resolve_embedding_api + from scion.cloud.embedding import resolve_embedding_api has_emb = bool(resolve_embedding_api()[0]) except Exception: diff --git a/openspace/config/README.md b/scion/config/README.md similarity index 100% rename from openspace/config/README.md rename to scion/config/README.md diff --git a/openspace/config/__init__.py b/scion/config/__init__.py similarity index 100% rename from openspace/config/__init__.py rename to scion/config/__init__.py diff --git a/openspace/config/config_agents.json b/scion/config/config_agents.json similarity index 100% rename from openspace/config/config_agents.json rename to scion/config/config_agents.json diff --git a/openspace/config/config_dev.json.example b/scion/config/config_dev.json.example similarity index 100% rename from openspace/config/config_dev.json.example rename to scion/config/config_dev.json.example diff --git a/openspace/config/config_grounding.json b/scion/config/config_grounding.json similarity index 100% rename from openspace/config/config_grounding.json rename to scion/config/config_grounding.json diff --git a/openspace/config/config_mcp.json.example b/scion/config/config_mcp.json.example similarity index 100% rename from openspace/config/config_mcp.json.example rename to scion/config/config_mcp.json.example diff --git a/openspace/config/config_security.json b/scion/config/config_security.json similarity index 100% rename from openspace/config/config_security.json rename to scion/config/config_security.json diff --git a/openspace/config/constants.py b/scion/config/constants.py similarity index 100% rename from openspace/config/constants.py rename to scion/config/constants.py diff --git a/openspace/config/grounding.py b/scion/config/grounding.py similarity index 98% rename from openspace/config/grounding.py rename to scion/config/grounding.py index 7eca780..1c0c943 100644 --- a/openspace/config/grounding.py +++ b/scion/config/grounding.py @@ -10,7 +10,7 @@ PYDANTIC_V2 = False -from openspace.grounding.core.types import BackendType, SecurityPolicy, SessionConfig +from scion.grounding.core.types import BackendType, SecurityPolicy, SessionConfig from .constants import LOG_LEVELS @@ -259,7 +259,7 @@ def get_backend_config(self, backend_type: str) -> BackendConfig: """Get configuration for specified backend""" name = backend_type.lower() if not hasattr(self, name): - from openspace.utils.logging import Logger + from scion.utils.logging import Logger logger = Logger.get_logger(__name__) logger.warning(f"Unknown backend type: {backend_type}") diff --git a/openspace/config/loader.py b/scion/config/loader.py similarity index 99% rename from openspace/config/loader.py rename to scion/config/loader.py index aaf96b1..ce3b749 100644 --- a/openspace/config/loader.py +++ b/scion/config/loader.py @@ -2,7 +2,7 @@ from pathlib import Path from typing import Any, Dict, Iterable, Optional, Union -from openspace.utils.logging import Logger +from scion.utils.logging import Logger from .constants import CONFIG_AGENTS, CONFIG_DEV, CONFIG_GROUNDING, CONFIG_MCP, CONFIG_SECURITY from .grounding import GroundingConfig diff --git a/openspace/config/utils.py b/scion/config/utils.py similarity index 100% rename from openspace/config/utils.py rename to scion/config/utils.py diff --git a/openspace/dashboard_server.py b/scion/dashboard_server.py similarity index 98% rename from openspace/dashboard_server.py rename to scion/dashboard_server.py index 301e53b..47b170a 100644 --- a/openspace/dashboard_server.py +++ b/scion/dashboard_server.py @@ -9,10 +9,10 @@ from flask import Flask, abort, jsonify, send_from_directory, url_for -from openspace.recording.action_recorder import analyze_agent_actions, load_agent_actions -from openspace.recording.utils import load_recording_session -from openspace.skill_engine import SkillStore -from openspace.skill_engine.types import SkillRecord +from scion.recording.action_recorder import analyze_agent_actions, load_agent_actions +from scion.recording.utils import load_recording_session +from scion.skill_engine import SkillStore +from scion.skill_engine.types import SkillRecord API_PREFIX = "/api/v1" FRONTEND_DIST_DIR = PROJECT_ROOT / "frontend" / "dist" diff --git a/openspace/domain/__init__.py b/scion/domain/__init__.py similarity index 78% rename from openspace/domain/__init__.py rename to scion/domain/__init__.py index 6f1aa60..6766771 100644 --- a/openspace/domain/__init__.py +++ b/scion/domain/__init__.py @@ -1,7 +1,7 @@ """Domain layer — ports (Protocol interfaces) and core value types. This package defines the contract boundary between the application core -and its infrastructure adapters. Nothing in ``openspace.domain`` should +and its infrastructure adapters. Nothing in ``scion.domain`` should import from adapter packages (``cloud``, ``grounding.backends``, ``llm``, ``recording``, ``local_server``, etc.). """ diff --git a/openspace/domain/enums.py b/scion/domain/enums.py similarity index 91% rename from openspace/domain/enums.py rename to scion/domain/enums.py index 27d0b3c..9727388 100644 --- a/openspace/domain/enums.py +++ b/scion/domain/enums.py @@ -3,22 +3,22 @@ Re-exports existing enums from their current locations for backward compatibility, and adds new enums that were previously magic strings. Existing code can continue importing from the original modules; new code -should prefer ``openspace.domain.enums``. +should prefer ``scion.domain.enums``. """ from __future__ import annotations from enum import Enum -from openspace.grounding.core.exceptions import ErrorCode as GroundingErrorCode -from openspace.grounding.core.types import ( +from scion.grounding.core.exceptions import ErrorCode as GroundingErrorCode +from scion.grounding.core.types import ( BackendType, SessionStatus, ToolStatus, ) # ── Re-exports from existing locations ──────────────────────────────── -from openspace.skill_engine.types import ( +from scion.skill_engine.types import ( EvolutionType, SkillCategory, SkillOrigin, diff --git a/openspace/domain/exceptions.py b/scion/domain/exceptions.py similarity index 99% rename from openspace/domain/exceptions.py rename to scion/domain/exceptions.py index 46c6dac..5c49abb 100644 --- a/openspace/domain/exceptions.py +++ b/scion/domain/exceptions.py @@ -6,7 +6,7 @@ Usage:: - from openspace.domain.exceptions import ValidationError, NotFoundError + from scion.domain.exceptions import ValidationError, NotFoundError raise ValidationError("skill_dirs must be a list") raise NotFoundError("skill", skill_id="abc-123") diff --git a/openspace/domain/logging.py b/scion/domain/logging.py similarity index 98% rename from openspace/domain/logging.py rename to scion/domain/logging.py index 5ebcc0f..a296fd5 100644 --- a/openspace/domain/logging.py +++ b/scion/domain/logging.py @@ -7,7 +7,7 @@ Usage — new code:: - from openspace.domain.logging import get_logger, bind_context + from scion.domain.logging import get_logger, bind_context log = get_logger(__name__) @@ -20,7 +20,7 @@ # stdlib loggers continue to work; structlog processors # will format their output through the shared formatter. import logging - logger = logging.getLogger("openspace.mcp_server") + logger = logging.getLogger("scion.mcp_server") logger.info("old-style message") # still works, gets structured formatting Context propagation uses :mod:`contextvars` so it is safe across @@ -279,7 +279,7 @@ def get_logger(name: Optional[str] = None) -> structlog.stdlib.BoundLogger: """ if not _configured: configure_logging() - return structlog.get_logger(name or "openspace") + return structlog.get_logger(name or "scion") __all__ = [ diff --git a/openspace/domain/ports.py b/scion/domain/ports.py similarity index 99% rename from openspace/domain/ports.py rename to scion/domain/ports.py index 66b18dc..f5b065d 100644 --- a/openspace/domain/ports.py +++ b/scion/domain/ports.py @@ -6,13 +6,13 @@ Usage:: - from openspace.domain.ports import SkillStorePort + from scion.domain.ports import SkillStorePort def some_service(store: SkillStorePort) -> None: record = store.load_record("skill-42") ... -All methods use domain types (``openspace.domain.types``) at the +All methods use domain types (``scion.domain.types``) at the boundary, **not** adapter-specific types. """ @@ -27,7 +27,7 @@ def some_service(store: SkillStorePort) -> None: runtime_checkable, ) -from openspace.domain.types import ( +from scion.domain.types import ( CapabilityLease, EvolutionRequest, EvolutionResult, diff --git a/openspace/domain/types.py b/scion/domain/types.py similarity index 99% rename from openspace/domain/types.py rename to scion/domain/types.py index c650286..aaa58fd 100644 --- a/openspace/domain/types.py +++ b/scion/domain/types.py @@ -4,7 +4,7 @@ shared across async boundaries, cached, and hashed. Mutations produce new instances via ``dataclasses.replace()``. -Existing mutable dataclasses in ``openspace.skill_engine.types`` remain +Existing mutable dataclasses in ``scion.skill_engine.types`` remain for backward compatibility. New code should prefer these frozen variants. """ diff --git a/openspace/errors.py b/scion/errors.py similarity index 93% rename from openspace/errors.py rename to scion/errors.py index d02b958..5174418 100644 --- a/openspace/errors.py +++ b/scion/errors.py @@ -23,7 +23,7 @@ import uuid from typing import Any -logger = logging.getLogger("openspace.mcp_server") +logger = logging.getLogger("scion.mcp_server") # ── Error codes ────────────────────────────────────────────────────── EXECUTION_ERROR = "EXECUTION_ERROR" @@ -70,7 +70,7 @@ def sanitize_error(exc: BaseException) -> str: sanitized = re.sub(r"\\\\[^\s\"']+", "", sanitized) # Unix-style absolute paths sanitized = re.sub(r"/(?:[\w.-]+/)+[\w.-]*", "", sanitized) - # Dotted module names (e.g. openspace.cloud.auth.TokenResolver) + # Dotted module names (e.g. scion.cloud.auth.TokenResolver) sanitized = re.sub(r"\b\w+(?:\.\w+){2,}\b", "", sanitized) # Standalone line-number references sanitized = re.sub(r"\bline \d+\b", "", sanitized) @@ -116,7 +116,7 @@ def handle_mcp_exception( ) -> str: """One-liner for MCP except blocks: log full traceback, return safe JSON. - For :class:`~openspace.domain.exceptions.OpenSpaceError` instances, + For :class:`~scion.domain.exceptions.OpenSpaceError` instances, uses ``client_message`` (never the raw message) and ``error_code`` from the exception. For all other exceptions, sanitizes via :func:`sanitize_error`. @@ -138,8 +138,8 @@ def handle_mcp_exception( # Prefer domain exception's safe client_message when available try: - from openspace.domain.exceptions import OpenSpaceError as _OSE - from openspace.domain.exceptions import map_to_mcp_error_code + from scion.domain.exceptions import OpenSpaceError as _OSE + from scion.domain.exceptions import map_to_mcp_error_code if isinstance(exc, _OSE): safe_msg = exc.client_message diff --git a/openspace/grounding/backends/__init__.py b/scion/grounding/backends/__init__.py similarity index 100% rename from openspace/grounding/backends/__init__.py rename to scion/grounding/backends/__init__.py diff --git a/openspace/grounding/backends/gui/__init__.py b/scion/grounding/backends/gui/__init__.py similarity index 100% rename from openspace/grounding/backends/gui/__init__.py rename to scion/grounding/backends/gui/__init__.py diff --git a/openspace/grounding/backends/gui/anthropic_client.py b/scion/grounding/backends/gui/anthropic_client.py similarity index 99% rename from openspace/grounding/backends/gui/anthropic_client.py rename to scion/grounding/backends/gui/anthropic_client.py index 2fd00b4..3254988 100644 --- a/openspace/grounding/backends/gui/anthropic_client.py +++ b/scion/grounding/backends/gui/anthropic_client.py @@ -6,7 +6,7 @@ from PIL import Image -from openspace.utils.logging import Logger +from scion.utils.logging import Logger logger = Logger.get_logger(__name__) diff --git a/openspace/grounding/backends/gui/anthropic_utils.py b/scion/grounding/backends/gui/anthropic_utils.py similarity index 99% rename from openspace/grounding/backends/gui/anthropic_utils.py rename to scion/grounding/backends/gui/anthropic_utils.py index 596d435..52f31ae 100644 --- a/openspace/grounding/backends/gui/anthropic_utils.py +++ b/scion/grounding/backends/gui/anthropic_utils.py @@ -2,7 +2,7 @@ from enum import Enum from typing import List, cast -from openspace.utils.logging import Logger +from scion.utils.logging import Logger logger = Logger.get_logger(__name__) diff --git a/openspace/grounding/backends/gui/config.py b/scion/grounding/backends/gui/config.py similarity index 98% rename from openspace/grounding/backends/gui/config.py rename to scion/grounding/backends/gui/config.py index e851713..31563c4 100644 --- a/openspace/grounding/backends/gui/config.py +++ b/scion/grounding/backends/gui/config.py @@ -2,7 +2,7 @@ import platform as platform_module from typing import Any, Dict, Optional -from openspace.utils.logging import Logger +from scion.utils.logging import Logger logger = Logger.get_logger(__name__) diff --git a/openspace/grounding/backends/gui/provider.py b/scion/grounding/backends/gui/provider.py similarity index 92% rename from openspace/grounding/backends/gui/provider.py rename to scion/grounding/backends/gui/provider.py index ce71fe4..57af1ff 100644 --- a/openspace/grounding/backends/gui/provider.py +++ b/scion/grounding/backends/gui/provider.py @@ -1,12 +1,12 @@ from typing import Any, Dict, Union -from openspace.config import get_config -from openspace.config.utils import get_config_value -from openspace.grounding.core.provider import Provider -from openspace.grounding.core.session import BaseSession -from openspace.grounding.core.types import BackendType, SessionConfig -from openspace.platforms import get_local_server_config -from openspace.utils.logging import Logger +from scion.config import get_config +from scion.config.utils import get_config_value +from scion.grounding.core.provider import Provider +from scion.grounding.core.session import BaseSession +from scion.grounding.core.types import BackendType, SessionConfig +from scion.platforms import get_local_server_config +from scion.utils.logging import Logger from .session import GUISession from .transport.connector import GUIConnector diff --git a/openspace/grounding/backends/gui/session.py b/scion/grounding/backends/gui/session.py similarity index 97% rename from openspace/grounding/backends/gui/session.py rename to scion/grounding/backends/gui/session.py index d4ccfa8..1dcc7f3 100644 --- a/openspace/grounding/backends/gui/session.py +++ b/scion/grounding/backends/gui/session.py @@ -1,9 +1,9 @@ import os from typing import Any, Dict, Union -from openspace.grounding.core.session import BaseSession -from openspace.grounding.core.types import BackendType, SessionConfig, SessionStatus -from openspace.utils.logging import Logger +from scion.grounding.core.session import BaseSession +from scion.grounding.core.types import BackendType, SessionConfig, SessionStatus +from scion.utils.logging import Logger from .config import build_llm_config from .tool import GUIAgentTool diff --git a/openspace/grounding/backends/gui/tool.py b/scion/grounding/backends/gui/tool.py similarity index 99% rename from openspace/grounding/backends/gui/tool.py rename to scion/grounding/backends/gui/tool.py index 0190b28..42271d4 100644 --- a/openspace/grounding/backends/gui/tool.py +++ b/scion/grounding/backends/gui/tool.py @@ -1,9 +1,9 @@ import base64 from typing import Any, Dict -from openspace.grounding.core.tool.base import BaseTool -from openspace.grounding.core.types import BackendType, ToolResult, ToolStatus -from openspace.utils.logging import Logger +from scion.grounding.core.tool.base import BaseTool +from scion.grounding.core.types import BackendType, ToolResult, ToolStatus +from scion.utils.logging import Logger from .transport.actions import ACTION_SPACE, KEYBOARD_KEYS from .transport.connector import GUIConnector @@ -560,7 +560,7 @@ async def _record_intermediate_step( # Check if recording is active try: - from openspace.recording.manager import RecordingManager + from scion.recording.manager import RecordingManager if not RecordingManager.is_recording(): logger.debug(f"Step {step_number}: RecordingManager not started") diff --git a/openspace/grounding/backends/gui/transport/actions.py b/scion/grounding/backends/gui/transport/actions.py similarity index 100% rename from openspace/grounding/backends/gui/transport/actions.py rename to scion/grounding/backends/gui/transport/actions.py diff --git a/openspace/grounding/backends/gui/transport/connector.py b/scion/grounding/backends/gui/transport/connector.py similarity index 99% rename from openspace/grounding/backends/gui/transport/connector.py rename to scion/grounding/backends/gui/transport/connector.py index cbd665e..b2c479a 100644 --- a/openspace/grounding/backends/gui/transport/connector.py +++ b/scion/grounding/backends/gui/transport/connector.py @@ -2,8 +2,8 @@ import re from typing import Any, Dict, Optional -from openspace.grounding.core.transport.connectors import AioHttpConnector -from openspace.utils.logging import Logger +from scion.grounding.core.transport.connectors import AioHttpConnector +from scion.utils.logging import Logger from .actions import KEYBOARD_KEYS, build_pyautogui_command diff --git a/openspace/grounding/backends/gui/transport/local_connector.py b/scion/grounding/backends/gui/transport/local_connector.py similarity index 97% rename from openspace/grounding/backends/gui/transport/local_connector.py rename to scion/grounding/backends/gui/transport/local_connector.py index 87f2301..97e2a91 100644 --- a/openspace/grounding/backends/gui/transport/local_connector.py +++ b/scion/grounding/backends/gui/transport/local_connector.py @@ -17,9 +17,9 @@ import uuid from typing import Any, Dict, Optional -from openspace.grounding.core.transport.connectors.base import BaseConnector -from openspace.grounding.core.transport.task_managers.noop import NoOpConnectionManager -from openspace.utils.logging import Logger +from scion.grounding.core.transport.connectors.base import BaseConnector +from scion.grounding.core.transport.task_managers.noop import NoOpConnectionManager +from scion.utils.logging import Logger logger = Logger.get_logger(__name__) @@ -59,14 +59,14 @@ def __init__( def _get_screenshot_helper(self): if self._screenshot_helper is None: - from openspace.local_server.utils import ScreenshotHelper + from scion.local_server.utils import ScreenshotHelper self._screenshot_helper = ScreenshotHelper() return self._screenshot_helper def _get_accessibility_helper(self): if self._accessibility_helper is None: - from openspace.local_server.utils import AccessibilityHelper + from scion.local_server.utils import AccessibilityHelper self._accessibility_helper = AccessibilityHelper() return self._accessibility_helper @@ -268,7 +268,7 @@ async def execute_action(self, action_type: str, parameters: Dict[str, Any] | No } # Import action builder (same module used by GUIConnector) - from openspace.grounding.backends.gui.transport.actions import ( + from scion.grounding.backends.gui.transport.actions import ( KEYBOARD_KEYS, build_pyautogui_command, ) diff --git a/openspace/grounding/backends/mcp/__init__.py b/scion/grounding/backends/mcp/__init__.py similarity index 100% rename from openspace/grounding/backends/mcp/__init__.py rename to scion/grounding/backends/mcp/__init__.py diff --git a/openspace/grounding/backends/mcp/client.py b/scion/grounding/backends/mcp/client.py similarity index 98% rename from openspace/grounding/backends/mcp/client.py rename to scion/grounding/backends/mcp/client.py index 0b19721..b8bcbbb 100644 --- a/openspace/grounding/backends/mcp/client.py +++ b/scion/grounding/backends/mcp/client.py @@ -9,9 +9,9 @@ import warnings from typing import Any, Optional -from openspace.config.utils import get_config_value, load_json_file, save_json_file -from openspace.grounding.core.types import SandboxOptions -from openspace.utils.logging import Logger +from scion.config.utils import get_config_value, load_json_file, save_json_file +from scion.grounding.core.types import SandboxOptions +from scion.utils.logging import Logger from .config import create_connector_from_config from .installer import MCPDependencyError, MCPInstallerManager diff --git a/openspace/grounding/backends/mcp/config.py b/scion/grounding/backends/mcp/config.py similarity index 97% rename from openspace/grounding/backends/mcp/config.py rename to scion/grounding/backends/mcp/config.py index 4b57092..d31aadc 100644 --- a/openspace/grounding/backends/mcp/config.py +++ b/scion/grounding/backends/mcp/config.py @@ -10,8 +10,8 @@ import os from typing import Any, Optional -from openspace.config.utils import get_config_value -from openspace.grounding.core.types import SandboxOptions +from scion.config.utils import get_config_value +from scion.grounding.core.types import SandboxOptions from .installer import MCPInstallerManager from .transport.connectors import ( @@ -25,7 +25,7 @@ # Import E2BSandbox try: - from openspace.grounding.core.security import E2BSandbox + from scion.grounding.core.security import E2BSandbox E2B_AVAILABLE = True except ImportError: diff --git a/openspace/grounding/backends/mcp/installer.py b/scion/grounding/backends/mcp/installer.py similarity index 99% rename from openspace/grounding/backends/mcp/installer.py rename to scion/grounding/backends/mcp/installer.py index db98754..1311d11 100644 --- a/openspace/grounding/backends/mcp/installer.py +++ b/scion/grounding/backends/mcp/installer.py @@ -3,7 +3,7 @@ import sys from typing import Awaitable, Callable, Dict, List, Optional -from openspace.utils.logging import Logger +from scion.utils.logging import Logger logger = Logger.get_logger(__name__) @@ -72,7 +72,7 @@ def __init__(self, prompt: PromptFunc | None = None, auto_install: bool = False, async def _default_cli_prompt(self, message: str) -> bool: """Default CLI prompt function (called within lock by ensure_dependencies).""" - from openspace.utils.display import colorize, print_separator + from scion.utils.display import colorize, print_separator print() print_separator(70, "c", 2) @@ -388,7 +388,7 @@ async def _install_package(self, command: str, args: List[str], use_sudo: bool = # For sudo commands, always show verbose output so password prompt is visible if self._verbose or use_sudo: # Verbose mode: show all installation logs - from openspace.utils.display import colorize, print_separator + from scion.utils.display import colorize, print_separator print_separator(70, "c", 2) if use_sudo: diff --git a/openspace/grounding/backends/mcp/provider.py b/scion/grounding/backends/mcp/provider.py similarity index 96% rename from openspace/grounding/backends/mcp/provider.py rename to scion/grounding/backends/mcp/provider.py index 1a5fb05..c306647 100644 --- a/openspace/grounding/backends/mcp/provider.py +++ b/scion/grounding/backends/mcp/provider.py @@ -7,16 +7,16 @@ import asyncio from typing import Dict, List, Optional -from openspace.config.utils import get_config_value -from openspace.grounding.backends.mcp.client import MCPClient -from openspace.grounding.backends.mcp.installer import MCPDependencyError, MCPInstallerManager -from openspace.grounding.backends.mcp.session import MCPSession -from openspace.grounding.backends.mcp.tool_cache import get_tool_cache -from openspace.grounding.backends.mcp.tool_converter import _sanitize_mcp_schema -from openspace.grounding.core.provider import Provider -from openspace.grounding.core.tool import BaseTool, RemoteTool -from openspace.grounding.core.types import BackendType, SessionConfig, ToolSchema -from openspace.utils.logging import Logger +from scion.config.utils import get_config_value +from scion.grounding.backends.mcp.client import MCPClient +from scion.grounding.backends.mcp.installer import MCPDependencyError, MCPInstallerManager +from scion.grounding.backends.mcp.session import MCPSession +from scion.grounding.backends.mcp.tool_cache import get_tool_cache +from scion.grounding.backends.mcp.tool_converter import _sanitize_mcp_schema +from scion.grounding.core.provider import Provider +from scion.grounding.core.tool import BaseTool, RemoteTool +from scion.grounding.core.types import BackendType, SessionConfig, ToolSchema +from scion.utils.logging import Logger logger = Logger.get_logger(__name__) diff --git a/openspace/grounding/backends/mcp/session.py b/scion/grounding/backends/mcp/session.py similarity index 87% rename from openspace/grounding/backends/mcp/session.py rename to scion/grounding/backends/mcp/session.py index d9e15ea..3b8686c 100644 --- a/openspace/grounding/backends/mcp/session.py +++ b/scion/grounding/backends/mcp/session.py @@ -7,11 +7,11 @@ from typing import Any, Dict -from openspace.grounding.backends.mcp.tool_converter import convert_mcp_tool_to_base_tool -from openspace.grounding.backends.mcp.transport.connectors import MCPBaseConnector -from openspace.grounding.core.session import BaseSession -from openspace.grounding.core.types import BackendType -from openspace.utils.logging import Logger +from scion.grounding.backends.mcp.tool_converter import convert_mcp_tool_to_base_tool +from scion.grounding.backends.mcp.transport.connectors import MCPBaseConnector +from scion.grounding.core.session import BaseSession +from scion.grounding.core.types import BackendType +from scion.utils.logging import Logger logger = Logger.get_logger(__name__) diff --git a/openspace/grounding/backends/mcp/tool_cache.py b/scion/grounding/backends/mcp/tool_cache.py similarity index 99% rename from openspace/grounding/backends/mcp/tool_cache.py rename to scion/grounding/backends/mcp/tool_cache.py index e45004c..074110a 100644 --- a/openspace/grounding/backends/mcp/tool_cache.py +++ b/scion/grounding/backends/mcp/tool_cache.py @@ -3,7 +3,7 @@ from pathlib import Path from typing import Any, Dict, List, Optional -from openspace.utils.logging import Logger +from scion.utils.logging import Logger logger = Logger.get_logger(__name__) diff --git a/openspace/grounding/backends/mcp/tool_converter.py b/scion/grounding/backends/mcp/tool_converter.py similarity index 96% rename from openspace/grounding/backends/mcp/tool_converter.py rename to scion/grounding/backends/mcp/tool_converter.py index 5ce0189..e28a747 100644 --- a/openspace/grounding/backends/mcp/tool_converter.py +++ b/scion/grounding/backends/mcp/tool_converter.py @@ -9,10 +9,10 @@ from mcp.types import Tool as MCPTool -from openspace.grounding.core.tool import BaseTool, RemoteTool -from openspace.grounding.core.transport.connectors import BaseConnector -from openspace.grounding.core.types import BackendType, ToolSchema -from openspace.utils.logging import Logger +from scion.grounding.core.tool import BaseTool, RemoteTool +from scion.grounding.core.transport.connectors import BaseConnector +from scion.grounding.core.types import BackendType, ToolSchema +from scion.utils.logging import Logger logger = Logger.get_logger(__name__) diff --git a/openspace/grounding/backends/mcp/transport/connectors/__init__.py b/scion/grounding/backends/mcp/transport/connectors/__init__.py similarity index 100% rename from openspace/grounding/backends/mcp/transport/connectors/__init__.py rename to scion/grounding/backends/mcp/transport/connectors/__init__.py diff --git a/openspace/grounding/backends/mcp/transport/connectors/base.py b/scion/grounding/backends/mcp/transport/connectors/base.py similarity index 98% rename from openspace/grounding/backends/mcp/transport/connectors/base.py rename to scion/grounding/backends/mcp/transport/connectors/base.py index 6116c22..40085ef 100644 --- a/openspace/grounding/backends/mcp/transport/connectors/base.py +++ b/scion/grounding/backends/mcp/transport/connectors/base.py @@ -12,9 +12,9 @@ from mcp.shared.exceptions import McpError from mcp.types import CallToolResult, GetPromptResult, Prompt, ReadResourceResult, Resource, Tool -from openspace.grounding.core.transport.connectors import BaseConnector -from openspace.grounding.core.transport.task_managers import BaseConnectionManager -from openspace.utils.logging import Logger +from scion.grounding.core.transport.connectors import BaseConnector +from scion.grounding.core.transport.task_managers import BaseConnectionManager +from scion.utils.logging import Logger logger = Logger.get_logger(__name__) diff --git a/openspace/grounding/backends/mcp/transport/connectors/http.py b/scion/grounding/backends/mcp/transport/connectors/http.py similarity index 98% rename from openspace/grounding/backends/mcp/transport/connectors/http.py rename to scion/grounding/backends/mcp/transport/connectors/http.py index 0c0055b..fdb4938 100644 --- a/openspace/grounding/backends/mcp/transport/connectors/http.py +++ b/scion/grounding/backends/mcp/transport/connectors/http.py @@ -22,16 +22,16 @@ Tool, ) -from openspace.grounding.backends.mcp.transport.connectors.base import ( +from scion.grounding.backends.mcp.transport.connectors.base import ( DEFAULT_TOOL_CALL_MAX_RETRIES, DEFAULT_TOOL_CALL_RETRY_DELAY, MCPBaseConnector, ) -from openspace.grounding.backends.mcp.transport.task_managers import ( +from scion.grounding.backends.mcp.transport.task_managers import ( SseConnectionManager, StreamableHttpConnectionManager, ) -from openspace.utils.logging import Logger +from scion.utils.logging import Logger logger = Logger.get_logger(__name__) @@ -80,7 +80,7 @@ def __init__( # Create a placeholder connection manager (will be set up later in connect()) # We use a placeholder here because the actual transport type (SSE vs Streamable HTTP) # can only be determined at runtime through server negotiation as per MCP specification - from openspace.grounding.core.transport.task_managers import PlaceholderConnectionManager + from scion.grounding.core.transport.task_managers import PlaceholderConnectionManager connection_manager = PlaceholderConnectionManager() super().__init__( diff --git a/openspace/grounding/backends/mcp/transport/connectors/sandbox.py b/scion/grounding/backends/mcp/transport/connectors/sandbox.py similarity index 95% rename from openspace/grounding/backends/mcp/transport/connectors/sandbox.py rename to scion/grounding/backends/mcp/transport/connectors/sandbox.py index f0d67d2..d311739 100644 --- a/openspace/grounding/backends/mcp/transport/connectors/sandbox.py +++ b/scion/grounding/backends/mcp/transport/connectors/sandbox.py @@ -12,11 +12,11 @@ import aiohttp from mcp import ClientSession -from openspace.grounding.backends.mcp.transport.connectors.base import MCPBaseConnector -from openspace.grounding.backends.mcp.transport.task_managers import SseConnectionManager -from openspace.grounding.core.security import BaseSandbox -from openspace.security.env_filter import ENV_ALLOWLIST -from openspace.utils.logging import Logger +from scion.grounding.backends.mcp.transport.connectors.base import MCPBaseConnector +from scion.grounding.backends.mcp.transport.task_managers import SseConnectionManager +from scion.grounding.core.security import BaseSandbox +from scion.security.env_filter import ENV_ALLOWLIST +from scion.utils.logging import Logger logger = Logger.get_logger(__name__) @@ -64,7 +64,7 @@ def __init__( # Create a placeholder connection manager (will be set up in connect()) # We need the sandbox to start first to get the base_url, so we can't create # the real SseConnectionManager until connect() is called - from openspace.grounding.core.transport.task_managers import PlaceholderConnectionManager + from scion.grounding.core.transport.task_managers import PlaceholderConnectionManager connection_manager = PlaceholderConnectionManager() super().__init__(connection_manager) diff --git a/openspace/grounding/backends/mcp/transport/connectors/stdio.py b/scion/grounding/backends/mcp/transport/connectors/stdio.py similarity index 98% rename from openspace/grounding/backends/mcp/transport/connectors/stdio.py rename to scion/grounding/backends/mcp/transport/connectors/stdio.py index 50de509..455bd97 100644 --- a/openspace/grounding/backends/mcp/transport/connectors/stdio.py +++ b/scion/grounding/backends/mcp/transport/connectors/stdio.py @@ -7,7 +7,7 @@ from mcp import StdioServerParameters -from openspace.utils.logging import Logger +from scion.utils.logging import Logger from ..task_managers import StdioConnectionManager from .base import MCPBaseConnector diff --git a/openspace/grounding/backends/mcp/transport/connectors/utils.py b/scion/grounding/backends/mcp/transport/connectors/utils.py similarity index 100% rename from openspace/grounding/backends/mcp/transport/connectors/utils.py rename to scion/grounding/backends/mcp/transport/connectors/utils.py diff --git a/openspace/grounding/backends/mcp/transport/connectors/websocket.py b/scion/grounding/backends/mcp/transport/connectors/websocket.py similarity index 99% rename from openspace/grounding/backends/mcp/transport/connectors/websocket.py rename to scion/grounding/backends/mcp/transport/connectors/websocket.py index 2d7dcb9..caa356e 100644 --- a/openspace/grounding/backends/mcp/transport/connectors/websocket.py +++ b/scion/grounding/backends/mcp/transport/connectors/websocket.py @@ -13,7 +13,7 @@ from mcp.types import Tool from websockets import ClientConnection -from openspace.utils.logging import Logger +from scion.utils.logging import Logger from ..task_managers import WebSocketConnectionManager from .base import MCPBaseConnector diff --git a/openspace/grounding/backends/mcp/transport/task_managers/__init__.py b/scion/grounding/backends/mcp/transport/task_managers/__init__.py similarity index 100% rename from openspace/grounding/backends/mcp/transport/task_managers/__init__.py rename to scion/grounding/backends/mcp/transport/task_managers/__init__.py diff --git a/openspace/grounding/backends/mcp/transport/task_managers/sse.py b/scion/grounding/backends/mcp/transport/task_managers/sse.py similarity index 93% rename from openspace/grounding/backends/mcp/transport/task_managers/sse.py rename to scion/grounding/backends/mcp/transport/task_managers/sse.py index 619e786..c9d2478 100644 --- a/openspace/grounding/backends/mcp/transport/task_managers/sse.py +++ b/scion/grounding/backends/mcp/transport/task_managers/sse.py @@ -9,10 +9,10 @@ from mcp.client.sse import sse_client -from openspace.grounding.core.transport.task_managers import ( +from scion.grounding.core.transport.task_managers import ( AsyncContextConnectionManager, ) -from openspace.utils.logging import Logger +from scion.utils.logging import Logger logger = Logger.get_logger(__name__) diff --git a/openspace/grounding/backends/mcp/transport/task_managers/stdio.py b/scion/grounding/backends/mcp/transport/task_managers/stdio.py similarity index 99% rename from openspace/grounding/backends/mcp/transport/task_managers/stdio.py rename to scion/grounding/backends/mcp/transport/task_managers/stdio.py index 441d60e..ac1410f 100644 --- a/openspace/grounding/backends/mcp/transport/task_managers/stdio.py +++ b/scion/grounding/backends/mcp/transport/task_managers/stdio.py @@ -14,10 +14,10 @@ from mcp import StdioServerParameters from mcp.client.stdio import stdio_client -from openspace.grounding.core.transport.task_managers import ( +from scion.grounding.core.transport.task_managers import ( AsyncContextConnectionManager, ) -from openspace.utils.logging import Logger +from scion.utils.logging import Logger logger = Logger.get_logger(__name__) diff --git a/openspace/grounding/backends/mcp/transport/task_managers/streamable_http.py b/scion/grounding/backends/mcp/transport/task_managers/streamable_http.py similarity index 97% rename from openspace/grounding/backends/mcp/transport/task_managers/streamable_http.py rename to scion/grounding/backends/mcp/transport/task_managers/streamable_http.py index b6a95ab..7318eb9 100644 --- a/openspace/grounding/backends/mcp/transport/task_managers/streamable_http.py +++ b/scion/grounding/backends/mcp/transport/task_managers/streamable_http.py @@ -11,10 +11,10 @@ from mcp.client.streamable_http import streamablehttp_client -from openspace.grounding.core.transport.task_managers import ( +from scion.grounding.core.transport.task_managers import ( AsyncContextConnectionManager, ) -from openspace.utils.logging import Logger +from scion.utils.logging import Logger logger = Logger.get_logger(__name__) diff --git a/openspace/grounding/backends/mcp/transport/task_managers/websocket.py b/scion/grounding/backends/mcp/transport/task_managers/websocket.py similarity index 88% rename from openspace/grounding/backends/mcp/transport/task_managers/websocket.py rename to scion/grounding/backends/mcp/transport/task_managers/websocket.py index 518f255..9086749 100644 --- a/openspace/grounding/backends/mcp/transport/task_managers/websocket.py +++ b/scion/grounding/backends/mcp/transport/task_managers/websocket.py @@ -8,10 +8,10 @@ from mcp.client.websocket import websocket_client -from openspace.grounding.core.transport.task_managers import ( +from scion.grounding.core.transport.task_managers import ( AsyncContextConnectionManager, ) -from openspace.utils.logging import Logger +from scion.utils.logging import Logger logger = Logger.get_logger(__name__) diff --git a/openspace/grounding/backends/shell/__init__.py b/scion/grounding/backends/shell/__init__.py similarity index 100% rename from openspace/grounding/backends/shell/__init__.py rename to scion/grounding/backends/shell/__init__.py diff --git a/openspace/grounding/backends/shell/productivity_tools.py b/scion/grounding/backends/shell/productivity_tools.py similarity index 97% rename from openspace/grounding/backends/shell/productivity_tools.py rename to scion/grounding/backends/shell/productivity_tools.py index f63feb4..8057372 100644 --- a/openspace/grounding/backends/shell/productivity_tools.py +++ b/scion/grounding/backends/shell/productivity_tools.py @@ -12,9 +12,9 @@ from pathlib import Path from typing import Any, Dict, List, Optional -from openspace.grounding.core.tool import BaseTool -from openspace.grounding.core.types import BackendType, ToolResult, ToolStatus -from openspace.utils.logging import Logger +from scion.grounding.core.tool import BaseTool +from scion.grounding.core.types import BackendType, ToolResult, ToolStatus +from scion.utils.logging import Logger logger = Logger.get_logger(__name__) @@ -47,7 +47,7 @@ def _set_global_state_for_productivity(data_path: str, current_date: str) -> Non if not _direct_tools: return _direct_tools.set_global_state( - signature="openspace", + signature="scion", economic_tracker=None, task_manager=None, evaluator=None, @@ -246,7 +246,7 @@ async def _arun( return ToolResult(status=ToolStatus.ERROR, content=f"Unsupported: {file_type}") try: - from openspace.grounding.backends.shell.session import _parse_shell_result + from scion.grounding.backends.shell.session import _parse_shell_result working_dir = getattr(self._session, "default_working_dir", None) result = await self._session.connector.run_python_script( @@ -378,7 +378,7 @@ async def _arun( ) try: - from openspace.grounding.backends.shell.session import _parse_shell_result + from scion.grounding.backends.shell.session import _parse_shell_result working_dir = getattr(self._session, "default_working_dir", None) result = await self._session.connector.run_python_script( diff --git a/openspace/grounding/backends/shell/provider.py b/scion/grounding/backends/shell/provider.py similarity index 92% rename from openspace/grounding/backends/shell/provider.py rename to scion/grounding/backends/shell/provider.py index e52c9f1..f456d0f 100644 --- a/openspace/grounding/backends/shell/provider.py +++ b/scion/grounding/backends/shell/provider.py @@ -1,9 +1,9 @@ -from openspace.config import get_config -from openspace.config.utils import get_config_value -from openspace.grounding.core.provider import Provider -from openspace.grounding.core.types import BackendType, SessionConfig -from openspace.platforms.config import get_local_server_config -from openspace.utils.logging import Logger +from scion.config import get_config +from scion.config.utils import get_config_value +from scion.grounding.core.provider import Provider +from scion.grounding.core.types import BackendType, SessionConfig +from scion.platforms.config import get_local_server_config +from scion.utils.logging import Logger from .session import ShellSession from .transport.connector import ShellConnector diff --git a/openspace/grounding/backends/shell/session.py b/scion/grounding/backends/shell/session.py similarity index 97% rename from openspace/grounding/backends/shell/session.py rename to scion/grounding/backends/shell/session.py index b272214..a573f5a 100644 --- a/openspace/grounding/backends/shell/session.py +++ b/scion/grounding/backends/shell/session.py @@ -2,14 +2,14 @@ import re from typing import Any, Tuple, Union -from openspace.grounding.backends.shell.transport.connector import ShellConnector -from openspace.grounding.backends.shell.transport.local_connector import LocalShellConnector -from openspace.grounding.core.security.policies import SecurityPolicyManager -from openspace.grounding.core.session import BaseSession -from openspace.grounding.core.tool import BaseTool -from openspace.grounding.core.types import BackendType, ToolResult, ToolStatus -from openspace.llm import LLMClient -from openspace.utils.logging import Logger +from scion.grounding.backends.shell.transport.connector import ShellConnector +from scion.grounding.backends.shell.transport.local_connector import LocalShellConnector +from scion.grounding.core.security.policies import SecurityPolicyManager +from scion.grounding.core.session import BaseSession +from scion.grounding.core.tool import BaseTool +from scion.grounding.core.types import BackendType, ToolResult, ToolStatus +from scion.llm import LLMClient +from scion.utils.logging import Logger logger = Logger.get_logger(__name__) @@ -67,7 +67,7 @@ async def initialize(self): if not self.use_clawwork_productivity: self.tools.insert(1, ReadFileTool(self)) if self.use_clawwork_productivity: - from openspace.grounding.backends.shell.productivity_tools import get_productivity_tools + from scion.grounding.backends.shell.productivity_tools import get_productivity_tools extra = get_productivity_tools( self, @@ -412,7 +412,7 @@ async def _get_system_info(self): if base_url is not None: try: - from openspace.platforms import SystemInfoClient + from scion.platforms import SystemInfoClient async with SystemInfoClient(base_url=base_url, timeout=5) as client: info = await client.get_system_info(use_cache=False) @@ -451,7 +451,7 @@ async def _get_system_info(self): return self._system_info async def _arun(self, task: str, timeout: int = 300): - from openspace.grounding.core.types import ToolResult, ToolStatus + from scion.grounding.core.types import ToolResult, ToolStatus sys_info = await self._get_system_info() conversation_history = [] diff --git a/openspace/grounding/backends/shell/transport/connector.py b/scion/grounding/backends/shell/transport/connector.py similarity index 95% rename from openspace/grounding/backends/shell/transport/connector.py rename to scion/grounding/backends/shell/transport/connector.py index 82d2679..1ee7a16 100644 --- a/openspace/grounding/backends/shell/transport/connector.py +++ b/scion/grounding/backends/shell/transport/connector.py @@ -1,9 +1,9 @@ import asyncio from typing import Any, Dict, Optional -from openspace.grounding.core.security import SecurityPolicyManager -from openspace.grounding.core.transport.connectors import AioHttpConnector -from openspace.utils.logging import Logger +from scion.grounding.core.security import SecurityPolicyManager +from scion.grounding.core.transport.connectors import AioHttpConnector +from scion.utils.logging import Logger logger = Logger.get_logger(__name__) @@ -120,7 +120,7 @@ async def run_python_script( RuntimeError: Execution failed or timed out """ if self._security_manager: - from openspace.grounding.core.types import BackendType + from scion.grounding.core.types import BackendType allowed = await self._security_manager.check_command_allowed(BackendType.SHELL, code) if not allowed: @@ -165,7 +165,7 @@ async def run_bash_script( RuntimeError: Execution failed or timed out """ if self._security_manager: - from openspace.grounding.core.types import BackendType + from scion.grounding.core.types import BackendType allowed = await self._security_manager.check_command_allowed(BackendType.SHELL, script) if not allowed: diff --git a/openspace/grounding/backends/shell/transport/local_connector.py b/scion/grounding/backends/shell/transport/local_connector.py similarity index 97% rename from openspace/grounding/backends/shell/transport/local_connector.py rename to scion/grounding/backends/shell/transport/local_connector.py index 4e60d2c..0358d4b 100644 --- a/openspace/grounding/backends/shell/transport/local_connector.py +++ b/scion/grounding/backends/shell/transport/local_connector.py @@ -15,10 +15,10 @@ import uuid from typing import Any, Dict, Optional -from openspace.grounding.core.security import SecurityPolicyManager -from openspace.grounding.core.transport.connectors.base import BaseConnector -from openspace.grounding.core.transport.task_managers.noop import NoOpConnectionManager -from openspace.utils.logging import Logger +from scion.grounding.core.security import SecurityPolicyManager +from scion.grounding.core.transport.connectors.base import BaseConnector +from scion.grounding.core.transport.task_managers.noop import NoOpConnectionManager +from scion.utils.logging import Logger logger = Logger.get_logger(__name__) @@ -256,7 +256,7 @@ async def run_python_script( """ # Security check if self._security_manager: - from openspace.grounding.core.types import BackendType + from scion.grounding.core.types import BackendType allowed = await self._security_manager.check_command_allowed(BackendType.SHELL, code) if not allowed: @@ -326,7 +326,7 @@ async def run_bash_script( """ # Security check if self._security_manager: - from openspace.grounding.core.types import BackendType + from scion.grounding.core.types import BackendType allowed = await self._security_manager.check_command_allowed(BackendType.SHELL, script) if not allowed: diff --git a/openspace/grounding/backends/web/__init__.py b/scion/grounding/backends/web/__init__.py similarity index 100% rename from openspace/grounding/backends/web/__init__.py rename to scion/grounding/backends/web/__init__.py diff --git a/openspace/grounding/backends/web/provider.py b/scion/grounding/backends/web/provider.py similarity index 91% rename from openspace/grounding/backends/web/provider.py rename to scion/grounding/backends/web/provider.py index d1694a3..507c18e 100644 --- a/openspace/grounding/backends/web/provider.py +++ b/scion/grounding/backends/web/provider.py @@ -1,8 +1,8 @@ from typing import Any, Dict -from openspace.grounding.core.provider import Provider -from openspace.grounding.core.types import BackendType, SessionConfig -from openspace.utils.logging import Logger +from scion.grounding.core.provider import Provider +from scion.grounding.core.types import BackendType, SessionConfig +from scion.utils.logging import Logger from .session import WebSession diff --git a/openspace/grounding/backends/web/session.py b/scion/grounding/backends/web/session.py similarity index 95% rename from openspace/grounding/backends/web/session.py rename to scion/grounding/backends/web/session.py index 3c685ac..b34eb6f 100644 --- a/openspace/grounding/backends/web/session.py +++ b/scion/grounding/backends/web/session.py @@ -4,14 +4,14 @@ from dotenv import load_dotenv -from openspace.grounding.core.session import BaseSession -from openspace.grounding.core.tool import BaseTool -from openspace.grounding.core.transport.connectors import BaseConnector -from openspace.grounding.core.types import BackendType, SessionConfig -from openspace.llm import LLMClient -from openspace.utils.logging import Logger - -# Load .env from openspace package root (4 levels up), then CWD fallback. +from scion.grounding.core.session import BaseSession +from scion.grounding.core.tool import BaseTool +from scion.grounding.core.transport.connectors import BaseConnector +from scion.grounding.core.types import BackendType, SessionConfig +from scion.llm import LLMClient +from scion.utils.logging import Logger + +# Load .env from scion package root (4 levels up), then CWD fallback. _PKG_ENV = Path(__file__).resolve().parent.parent.parent.parent / ".env" # openspace/.env if _PKG_ENV.is_file(): load_dotenv(_PKG_ENV) diff --git a/openspace/grounding/core/exceptions.py b/scion/grounding/core/exceptions.py similarity index 100% rename from openspace/grounding/core/exceptions.py rename to scion/grounding/core/exceptions.py diff --git a/openspace/grounding/core/grounding_client.py b/scion/grounding/core/grounding_client.py similarity index 99% rename from openspace/grounding/core/grounding_client.py rename to scion/grounding/core/grounding_client.py index d1045ed..3da09c0 100644 --- a/openspace/grounding/core/grounding_client.py +++ b/scion/grounding/core/grounding_client.py @@ -5,9 +5,9 @@ from datetime import datetime from typing import Any, Dict, List, Optional -from openspace.config import GroundingConfig, get_config -from openspace.config.utils import get_config_value -from openspace.utils.logging import Logger +from scion.config import GroundingConfig, get_config +from scion.config.utils import get_config_value +from scion.utils.logging import Logger from .exceptions import ErrorCode, GroundingError from .provider import Provider, ProviderRegistry @@ -127,7 +127,7 @@ def _init_quality_manager(self): from pathlib import Path - from openspace.config.constants import PROJECT_ROOT + from scion.config.constants import PROJECT_ROOT from .quality import ToolQualityManager, set_quality_manager @@ -139,7 +139,7 @@ def _init_quality_manager(self): # Default: same location as SkillStore db_dir = PROJECT_ROOT / ".openspace" db_dir.mkdir(parents=True, exist_ok=True) - db_path = db_dir / "openspace.db" + db_path = db_dir / "scion.db" manager = ToolQualityManager( db_path=db_path, diff --git a/openspace/grounding/core/provider.py b/scion/grounding/core/provider.py similarity index 98% rename from openspace/grounding/core/provider.py rename to scion/grounding/core/provider.py index 271cd69..fcb0c1e 100644 --- a/openspace/grounding/core/provider.py +++ b/scion/grounding/core/provider.py @@ -5,8 +5,8 @@ from abc import ABC, abstractmethod from typing import Any, Dict, Generic, List, Optional, TypeVar -from openspace.config import get_config -from openspace.utils.logging import Logger +from scion.config import get_config +from scion.utils.logging import Logger from .security.policies import SecurityPolicyManager from .session import BaseSession diff --git a/openspace/grounding/core/quality/__init__.py b/scion/grounding/core/quality/__init__.py similarity index 100% rename from openspace/grounding/core/quality/__init__.py rename to scion/grounding/core/quality/__init__.py diff --git a/openspace/grounding/core/quality/manager.py b/scion/grounding/core/quality/manager.py similarity index 99% rename from openspace/grounding/core/quality/manager.py rename to scion/grounding/core/quality/manager.py index 841775d..1da8dab 100644 --- a/openspace/grounding/core/quality/manager.py +++ b/scion/grounding/core/quality/manager.py @@ -15,15 +15,15 @@ from pathlib import Path from typing import TYPE_CHECKING, Dict, List, Optional, Tuple -from openspace.utils.logging import Logger +from scion.utils.logging import Logger from .store import QualityStore from .types import DescriptionQuality, ExecutionRecord, ToolQualityRecord if TYPE_CHECKING: - from openspace.grounding.core.tool import BaseTool - from openspace.grounding.core.types import ToolResult - from openspace.llm import LLMClient + from scion.grounding.core.tool import BaseTool + from scion.grounding.core.types import ToolResult + from scion.llm import LLMClient logger = Logger.get_logger(__name__) @@ -77,7 +77,7 @@ def __init__( def get_tool_key(self, tool: "BaseTool") -> str: """Generate unique key for a tool.""" - from openspace.grounding.core.types import BackendType + from scion.grounding.core.types import BackendType if tool.is_bound: backend = tool.runtime_info.backend.value diff --git a/openspace/grounding/core/quality/store.py b/scion/grounding/core/quality/store.py similarity index 97% rename from openspace/grounding/core/quality/store.py rename to scion/grounding/core/quality/store.py index 669c733..893b29b 100644 --- a/openspace/grounding/core/quality/store.py +++ b/scion/grounding/core/quality/store.py @@ -2,7 +2,7 @@ SQLite-backed persistence for tool quality data. Shares the same database file as SkillStore. Storage location (default): - /.openspace/openspace.db + /.openspace/scion.db Tables managed by this module: tool_quality_records — one row per tool (aggregate stats) @@ -16,8 +16,8 @@ from pathlib import Path from typing import Dict, Optional, Tuple -from openspace.config.constants import PROJECT_ROOT -from openspace.utils.logging import Logger +from scion.config.constants import PROJECT_ROOT +from scion.utils.logging import Logger from .types import DescriptionQuality, ExecutionRecord, ToolQualityRecord @@ -68,7 +68,7 @@ class QualityStore: """SQLite-backed persistence for tool quality data. By default uses the same ``.db`` file as ``SkillStore`` - (``/.openspace/openspace.db``). + (``/.openspace/scion.db``). Each subsystem creates its own tables independently. """ @@ -76,7 +76,7 @@ def __init__(self, db_path: Optional[Path] = None): if db_path is None: db_dir = PROJECT_ROOT / ".openspace" db_dir.mkdir(parents=True, exist_ok=True) - db_path = db_dir / "openspace.db" + db_path = db_dir / "scion.db" self._db_path = Path(db_path) self._mu = threading.Lock() diff --git a/openspace/grounding/core/quality/types.py b/scion/grounding/core/quality/types.py similarity index 100% rename from openspace/grounding/core/quality/types.py rename to scion/grounding/core/quality/types.py diff --git a/openspace/grounding/core/search_tools.py b/scion/grounding/core/search_tools.py similarity index 99% rename from openspace/grounding/core/search_tools.py rename to scion/grounding/core/search_tools.py index 2ce90ef..a5de422 100644 --- a/openspace/grounding/core/search_tools.py +++ b/scion/grounding/core/search_tools.py @@ -10,10 +10,10 @@ import httpx import numpy as np -from openspace.config.constants import PROJECT_ROOT -from openspace.grounding.core.tool.base import BaseTool -from openspace.llm import LLMClient -from openspace.utils.logging import Logger +from scion.config.constants import PROJECT_ROOT +from scion.grounding.core.tool.base import BaseTool +from scion.llm import LLMClient +from scion.utils.logging import Logger from .tool import BaseTool from .types import BackendType @@ -62,7 +62,7 @@ def __init__( if model_name is None: try: - from openspace.config import get_config + from scion.config import get_config config = get_config() model_name = config.tool_search.embedding_model @@ -582,7 +582,7 @@ def __init__( # Load config (may be None if loading fails) tool_search_config = None try: - from openspace.config import get_config + from scion.config import get_config tool_search_config = getattr(get_config(), "tool_search", None) except Exception as exc: diff --git a/openspace/grounding/core/security/__init__.py b/scion/grounding/core/security/__init__.py similarity index 100% rename from openspace/grounding/core/security/__init__.py rename to scion/grounding/core/security/__init__.py diff --git a/openspace/grounding/core/security/e2b_sandbox.py b/scion/grounding/core/security/e2b_sandbox.py similarity index 99% rename from openspace/grounding/core/security/e2b_sandbox.py rename to scion/grounding/core/security/e2b_sandbox.py index c4a01b3..c0f5da9 100644 --- a/openspace/grounding/core/security/e2b_sandbox.py +++ b/scion/grounding/core/security/e2b_sandbox.py @@ -7,7 +7,7 @@ import os from typing import TYPE_CHECKING, Any -from openspace.utils.logging import Logger +from scion.utils.logging import Logger from ..types import SandboxOptions from .sandbox import BaseSandbox diff --git a/openspace/grounding/core/security/policies.py b/scion/grounding/core/security/policies.py similarity index 97% rename from openspace/grounding/core/security/policies.py rename to scion/grounding/core/security/policies.py index fb530b6..00b14aa 100644 --- a/openspace/grounding/core/security/policies.py +++ b/scion/grounding/core/security/policies.py @@ -27,7 +27,7 @@ def __init__(self, prompt: PromptFunc | None = None): async def _default_cli_prompt(self, message: str) -> bool: # Clean and professional prompt using unified display - from openspace.utils.display import colorize, print_separator + from scion.utils.display import colorize, print_separator print() print_separator(70, "y", 2) @@ -121,7 +121,7 @@ async def check_command_allowed(self, backend_type: BackendType, command: str) - # Show which dangerous commands were detected dangerous_list = ", ".join([f"{Colors.RED}{tok}{Colors.RESET}" for tok in dangerous_tokens[:5]]) - from openspace.utils.display import Box, BoxStyle, colorize + from scion.utils.display import Box, BoxStyle, colorize # Build command box box = Box(width=66, style=BoxStyle.SQUARE, color="gr") diff --git a/openspace/grounding/core/security/sandbox.py b/scion/grounding/core/security/sandbox.py similarity index 100% rename from openspace/grounding/core/security/sandbox.py rename to scion/grounding/core/security/sandbox.py diff --git a/openspace/grounding/core/session.py b/scion/grounding/core/session.py similarity index 98% rename from openspace/grounding/core/session.py rename to scion/grounding/core/session.py index 1c70c91..6e35bcf 100644 --- a/openspace/grounding/core/session.py +++ b/scion/grounding/core/session.py @@ -2,7 +2,7 @@ from datetime import datetime from typing import Any, Dict, List -from openspace.utils.logging import Logger +from scion.utils.logging import Logger from .tool import BaseTool from .transport.connectors import BaseConnector diff --git a/openspace/grounding/core/system/__init__.py b/scion/grounding/core/system/__init__.py similarity index 100% rename from openspace/grounding/core/system/__init__.py rename to scion/grounding/core/system/__init__.py diff --git a/openspace/grounding/core/system/provider.py b/scion/grounding/core/system/provider.py similarity index 100% rename from openspace/grounding/core/system/provider.py rename to scion/grounding/core/system/provider.py diff --git a/openspace/grounding/core/system/tool.py b/scion/grounding/core/system/tool.py similarity index 100% rename from openspace/grounding/core/system/tool.py rename to scion/grounding/core/system/tool.py diff --git a/openspace/grounding/core/tool/__init__.py b/scion/grounding/core/tool/__init__.py similarity index 100% rename from openspace/grounding/core/tool/__init__.py rename to scion/grounding/core/tool/__init__.py diff --git a/openspace/grounding/core/tool/base.py b/scion/grounding/core/tool/base.py similarity index 98% rename from openspace/grounding/core/tool/base.py rename to scion/grounding/core/tool/base.py index 32825ad..a40f2ff 100644 --- a/openspace/grounding/core/tool/base.py +++ b/scion/grounding/core/tool/base.py @@ -14,7 +14,7 @@ import jsonschema from pydantic import BaseModel, ConfigDict, Field, create_model -from openspace.utils.logging import Logger +from scion.utils.logging import Logger from ..exceptions import ErrorCode, GroundingError from ..types import BackendType, ToolResult, ToolSchema, ToolStatus @@ -239,7 +239,7 @@ async def _auto_record_execution( # Record to recording manager (for trajectory recording) try: - from openspace.recording import RecordingManager + from scion.recording import RecordingManager if not RecordingManager.is_recording(): return @@ -285,7 +285,7 @@ async def _record_to_quality_manager( ): """Record execution result to quality manager for quality tracking.""" try: - from openspace.grounding.core.quality import get_quality_manager + from scion.grounding.core.quality import get_quality_manager manager = get_quality_manager() if manager: diff --git a/openspace/grounding/core/tool/local_tool.py b/scion/grounding/core/tool/local_tool.py similarity index 100% rename from openspace/grounding/core/tool/local_tool.py rename to scion/grounding/core/tool/local_tool.py diff --git a/openspace/grounding/core/tool/remote_tool.py b/scion/grounding/core/tool/remote_tool.py similarity index 96% rename from openspace/grounding/core/tool/remote_tool.py rename to scion/grounding/core/tool/remote_tool.py index 6e162ef..4ccf5cc 100644 --- a/openspace/grounding/core/tool/remote_tool.py +++ b/scion/grounding/core/tool/remote_tool.py @@ -5,8 +5,8 @@ from typing import Optional -from openspace.grounding.core.transport.connectors import BaseConnector -from openspace.utils.logging import Logger +from scion.grounding.core.transport.connectors import BaseConnector +from scion.utils.logging import Logger from ..types import BackendType, ToolResult, ToolSchema, ToolStatus from .base import BaseTool diff --git a/openspace/grounding/core/transport/connectors/__init__.py b/scion/grounding/core/transport/connectors/__init__.py similarity index 100% rename from openspace/grounding/core/transport/connectors/__init__.py rename to scion/grounding/core/transport/connectors/__init__.py diff --git a/openspace/grounding/core/transport/connectors/aiohttp_connector.py b/scion/grounding/core/transport/connectors/aiohttp_connector.py similarity index 99% rename from openspace/grounding/core/transport/connectors/aiohttp_connector.py rename to scion/grounding/core/transport/connectors/aiohttp_connector.py index da7e140..0d8aa75 100644 --- a/openspace/grounding/core/transport/connectors/aiohttp_connector.py +++ b/scion/grounding/core/transport/connectors/aiohttp_connector.py @@ -4,7 +4,7 @@ from pydantic import BaseModel from yarl import URL -from openspace.utils.logging import Logger +from scion.utils.logging import Logger from ..task_managers import AioHttpConnectionManager from .base import BaseConnector diff --git a/openspace/grounding/core/transport/connectors/base.py b/scion/grounding/core/transport/connectors/base.py similarity index 100% rename from openspace/grounding/core/transport/connectors/base.py rename to scion/grounding/core/transport/connectors/base.py diff --git a/openspace/grounding/core/transport/task_managers/__init__.py b/scion/grounding/core/transport/task_managers/__init__.py similarity index 100% rename from openspace/grounding/core/transport/task_managers/__init__.py rename to scion/grounding/core/transport/task_managers/__init__.py diff --git a/openspace/grounding/core/transport/task_managers/aiohttp_connection_manager.py b/scion/grounding/core/transport/task_managers/aiohttp_connection_manager.py similarity index 100% rename from openspace/grounding/core/transport/task_managers/aiohttp_connection_manager.py rename to scion/grounding/core/transport/task_managers/aiohttp_connection_manager.py diff --git a/openspace/grounding/core/transport/task_managers/async_ctx.py b/scion/grounding/core/transport/task_managers/async_ctx.py similarity index 100% rename from openspace/grounding/core/transport/task_managers/async_ctx.py rename to scion/grounding/core/transport/task_managers/async_ctx.py diff --git a/openspace/grounding/core/transport/task_managers/base.py b/scion/grounding/core/transport/task_managers/base.py similarity index 99% rename from openspace/grounding/core/transport/task_managers/base.py rename to scion/grounding/core/transport/task_managers/base.py index 5c8aece..26f1359 100644 --- a/openspace/grounding/core/transport/task_managers/base.py +++ b/scion/grounding/core/transport/task_managers/base.py @@ -11,7 +11,7 @@ from abc import ABC, abstractmethod from typing import Generic, TypeVar -from openspace.utils.logging import Logger +from scion.utils.logging import Logger T = TypeVar("T") diff --git a/openspace/grounding/core/transport/task_managers/noop.py b/scion/grounding/core/transport/task_managers/noop.py similarity index 100% rename from openspace/grounding/core/transport/task_managers/noop.py rename to scion/grounding/core/transport/task_managers/noop.py diff --git a/openspace/grounding/core/transport/task_managers/placeholder.py b/scion/grounding/core/transport/task_managers/placeholder.py similarity index 100% rename from openspace/grounding/core/transport/task_managers/placeholder.py rename to scion/grounding/core/transport/task_managers/placeholder.py diff --git a/openspace/grounding/core/types.py b/scion/grounding/core/types.py similarity index 100% rename from openspace/grounding/core/types.py rename to scion/grounding/core/types.py diff --git a/openspace/host_detection/__init__.py b/scion/host_detection/__init__.py similarity index 79% rename from openspace/host_detection/__init__.py rename to scion/host_detection/__init__.py index 47572ce..b57d948 100644 --- a/openspace/host_detection/__init__.py +++ b/scion/host_detection/__init__.py @@ -14,38 +14,38 @@ Supported host agents: - - **nanobot** — ``~/.nanobot/config.json`` (``tools.mcpServers.openspace.env``) - - **openclaw** — ``~/.openclaw/openclaw.json`` (``skills.entries.openspace.env``) + - **nanobot** — ``~/.nanobot/config.json`` (``tools.mcpServers.scion.env``) + - **openclaw** — ``~/.openclaw/openclaw.json`` (``skills.entries.scion.env``) """ import logging from typing import Dict, Optional -from openspace.host_detection.nanobot import ( +from scion.host_detection.nanobot import ( get_openai_api_key as _nanobot_get_openai_api_key, ) -from openspace.host_detection.nanobot import ( +from scion.host_detection.nanobot import ( read_nanobot_mcp_env, try_read_nanobot_config, ) -from openspace.host_detection.openclaw import ( +from scion.host_detection.openclaw import ( get_openclaw_openai_api_key as _openclaw_get_openai_api_key, ) -from openspace.host_detection.openclaw import ( +from scion.host_detection.openclaw import ( is_openclaw_host, read_openclaw_skill_env, ) -from openspace.host_detection.resolver import build_grounding_config_path, build_llm_kwargs +from scion.host_detection.resolver import build_grounding_config_path, build_llm_kwargs -logger = logging.getLogger("openspace.host_detection") +logger = logging.getLogger("scion.host_detection") def read_host_mcp_env() -> Dict[str, str]: """Read the OpenSpace env block from the current host agent config. Resolution order: - 1. nanobot — ``tools.mcpServers.openspace.env`` - 2. openclaw — ``skills.entries.openspace.env`` + 1. nanobot — ``tools.mcpServers.scion.env`` + 2. openclaw — ``skills.entries.scion.env`` 3. Empty dict (no host detected) Callers (e.g. ``cloud.auth``) use this single entry point and never @@ -57,7 +57,7 @@ def read_host_mcp_env() -> Dict[str, str]: return env # Try openclaw - env = read_openclaw_skill_env("openspace") + env = read_openclaw_skill_env("scion") if env: logger.debug("read_host_mcp_env: resolved from OpenClaw config") return env @@ -71,7 +71,7 @@ def get_openai_api_key() -> Optional[str]: Resolution: 1. ``OPENAI_API_KEY`` env var (checked inside nanobot reader) 2. nanobot config ``providers.openai.apiKey`` - 3. openclaw config ``skills.entries.openspace.env.OPENAI_API_KEY`` + 3. openclaw config ``skills.entries.scion.env.OPENAI_API_KEY`` 4. None """ # nanobot reader already checks OPENAI_API_KEY env var first diff --git a/openspace/host_detection/nanobot.py b/scion/host_detection/nanobot.py similarity index 97% rename from openspace/host_detection/nanobot.py rename to scion/host_detection/nanobot.py index ece9bab..a5e976d 100644 --- a/openspace/host_detection/nanobot.py +++ b/scion/host_detection/nanobot.py @@ -15,7 +15,7 @@ from pathlib import Path from typing import Any, Dict, List, Optional -logger = logging.getLogger("openspace.host_detection") +logger = logging.getLogger("scion.host_detection") PROVIDER_REGISTRY: List[tuple] = [ # Gateways @@ -165,7 +165,7 @@ def try_read_nanobot_config(model: str) -> Optional[Dict[str, Any]]: def read_nanobot_mcp_env() -> Dict[str, str]: - """Read ``tools.mcpServers.openspace.env`` from nanobot config. + """Read ``tools.mcpServers.scion.env`` from nanobot config. Returns the env dict (empty if not found / parse error). """ @@ -179,7 +179,7 @@ def read_nanobot_mcp_env() -> Dict[str, str]: mcp_servers = tools.get("mcpServers") or tools.get("mcp_servers") or {} if not isinstance(mcp_servers, dict): return {} - openspace_cfg = mcp_servers.get("openspace", {}) + openspace_cfg = mcp_servers.get("scion", {}) if not isinstance(openspace_cfg, dict): return {} env_block = openspace_cfg.get("env", {}) diff --git a/openspace/host_detection/openclaw.py b/scion/host_detection/openclaw.py similarity index 91% rename from openspace/host_detection/openclaw.py rename to scion/host_detection/openclaw.py index 3f6a59b..34595bd 100644 --- a/openspace/host_detection/openclaw.py +++ b/scion/host_detection/openclaw.py @@ -2,7 +2,7 @@ Reads ``~/.openclaw/openclaw.json`` to auto-detect: - LLM provider credentials (via ``auth-profiles`` — not yet implemented) - - Skill-level env block (``skills.entries.openspace.env``) + - Skill-level env block (``skills.entries.scion.env``) - OpenAI API key for embedding generation Config path resolution mirrors OpenClaw's own logic: @@ -20,7 +20,7 @@ from pathlib import Path from typing import Any, Dict, Optional -logger = logging.getLogger("openspace.host_detection") +logger = logging.getLogger("scion.host_detection") _STATE_DIRNAMES = [".openclaw", ".clawdbot", ".moldbot", ".moltbot"] _CONFIG_FILENAMES = ["openclaw.json", "clawdbot.json", "moldbot.json", "moltbot.json"] @@ -71,11 +71,11 @@ def _load_openclaw_config() -> Optional[Dict[str, Any]]: return None -def read_openclaw_skill_env(skill_name: str = "openspace") -> Dict[str, str]: +def read_openclaw_skill_env(skill_name: str = "scion") -> Dict[str, str]: """Read ``skills.entries..env`` from OpenClaw config. This is the OpenClaw equivalent of nanobot's - ``tools.mcpServers.openspace.env``. + ``tools.mcpServers.scion.env``. Returns the env dict (empty if not found / parse error). """ @@ -99,13 +99,13 @@ def read_openclaw_skill_env(skill_name: str = "openspace") -> Dict[str, str]: def get_openclaw_openai_api_key() -> Optional[str]: """Get OpenAI API key from OpenClaw config. - Checks ``skills.entries.openspace.env.OPENAI_API_KEY`` first, + Checks ``skills.entries.scion.env.OPENAI_API_KEY`` first, then any top-level env vars in the config. Returns the key string, or None. """ # Try skill-level env - env = read_openclaw_skill_env("openspace") + env = read_openclaw_skill_env("scion") key = env.get("OPENAI_API_KEY", "").strip() if key: logger.debug("Using OpenAI API key from OpenClaw skill env config") diff --git a/openspace/host_detection/resolver.py b/scion/host_detection/resolver.py similarity index 98% rename from openspace/host_detection/resolver.py rename to scion/host_detection/resolver.py index d0b0de8..a67b074 100644 --- a/openspace/host_detection/resolver.py +++ b/scion/host_detection/resolver.py @@ -12,7 +12,7 @@ import tempfile from typing import Any, Dict, Optional -logger = logging.getLogger("openspace.host_detection") +logger = logging.getLogger("scion.host_detection") def build_llm_kwargs(model: str) -> tuple[str, Dict[str, Any]]: @@ -37,7 +37,7 @@ def build_llm_kwargs(model: str) -> tuple[str, Dict[str, Any]]: Returns: ``(resolved_model, llm_kwargs_dict)`` """ - from openspace.host_detection.nanobot import try_read_nanobot_config + from scion.host_detection.nanobot import try_read_nanobot_config kwargs: Dict[str, Any] = {} resolved_model = model diff --git a/openspace/host_skills/README.md b/scion/host_skills/README.md similarity index 100% rename from openspace/host_skills/README.md rename to scion/host_skills/README.md diff --git a/openspace/host_skills/delegate-task/SKILL.md b/scion/host_skills/delegate-task/SKILL.md similarity index 100% rename from openspace/host_skills/delegate-task/SKILL.md rename to scion/host_skills/delegate-task/SKILL.md diff --git a/openspace/host_skills/skill-discovery/SKILL.md b/scion/host_skills/skill-discovery/SKILL.md similarity index 100% rename from openspace/host_skills/skill-discovery/SKILL.md rename to scion/host_skills/skill-discovery/SKILL.md diff --git a/openspace/llm/__init__.py b/scion/llm/__init__.py similarity index 100% rename from openspace/llm/__init__.py rename to scion/llm/__init__.py diff --git a/openspace/llm/client.py b/scion/llm/client.py similarity index 99% rename from openspace/llm/client.py rename to scion/llm/client.py index 9a4d2e9..ba6861b 100644 --- a/openspace/llm/client.py +++ b/scion/llm/client.py @@ -8,11 +8,11 @@ from dotenv import load_dotenv from openai.types.chat import ChatCompletionToolParam -from openspace.grounding.core.tool import BaseTool -from openspace.grounding.core.types import ToolResult, ToolSchema, ToolStatus -from openspace.utils.logging import Logger +from scion.grounding.core.tool import BaseTool +from scion.grounding.core.types import ToolResult, ToolSchema, ToolStatus +from scion.utils.logging import Logger -# Load .env from openspace package root (works regardless of CWD), +# Load .env from scion package root (works regardless of CWD), # then fall back to CWD/.env. override=False (default) means first-loaded wins. _PKG_ENV = Path(__file__).resolve().parent.parent / ".env" # openspace/.env if _PKG_ENV.is_file(): diff --git a/openspace/local_server/README.md b/scion/local_server/README.md similarity index 100% rename from openspace/local_server/README.md rename to scion/local_server/README.md diff --git a/openspace/local_server/__init__.py b/scion/local_server/__init__.py similarity index 100% rename from openspace/local_server/__init__.py rename to scion/local_server/__init__.py diff --git a/openspace/local_server/config.json b/scion/local_server/config.json similarity index 100% rename from openspace/local_server/config.json rename to scion/local_server/config.json diff --git a/openspace/local_server/feature_checker.py b/scion/local_server/feature_checker.py similarity index 99% rename from openspace/local_server/feature_checker.py rename to scion/local_server/feature_checker.py index 57d5cca..baa1813 100644 --- a/openspace/local_server/feature_checker.py +++ b/scion/local_server/feature_checker.py @@ -3,7 +3,7 @@ import tempfile from typing import Any, Dict -from openspace.utils.logging import Logger +from scion.utils.logging import Logger logger = Logger.get_logger(__name__) diff --git a/openspace/local_server/health_checker.py b/scion/local_server/health_checker.py similarity index 98% rename from openspace/local_server/health_checker.py rename to scion/local_server/health_checker.py index 3c346ae..4cf437d 100644 --- a/openspace/local_server/health_checker.py +++ b/scion/local_server/health_checker.py @@ -4,12 +4,12 @@ import requests -from openspace.local_server.feature_checker import FeatureChecker -from openspace.utils.logging import Logger +from scion.local_server.feature_checker import FeatureChecker +from scion.utils.logging import Logger logger = Logger.get_logger(__name__) -from openspace.utils.display import colorize as _c +from scion.utils.display import colorize as _c class HealthStatus: @@ -535,7 +535,7 @@ def print_results(self, results: Dict[str, HealthStatus] = None, show_endpoint_d print(f" {_c('·', 'gr')} {name}: {_c(str(status), 'gr')}") # Summary - from openspace.utils.display import print_separator + from scion.utils.display import print_separator print() print_separator() diff --git a/openspace/local_server/main.py b/scion/local_server/main.py similarity index 98% rename from openspace/local_server/main.py rename to scion/local_server/main.py index 62530c6..599ad0d 100644 --- a/openspace/local_server/main.py +++ b/scion/local_server/main.py @@ -14,11 +14,11 @@ import pyautogui from flask import Flask, abort, jsonify, request, send_file -from openspace.local_server.feature_checker import FeatureChecker -from openspace.local_server.health_checker import HealthChecker -from openspace.local_server.platform_adapters import get_platform_adapter -from openspace.local_server.utils import AccessibilityHelper, ScreenshotHelper -from openspace.utils.logging import Logger +from scion.local_server.feature_checker import FeatureChecker +from scion.local_server.health_checker import HealthChecker +from scion.local_server.platform_adapters import get_platform_adapter +from scion.local_server.utils import AccessibilityHelper, ScreenshotHelper +from scion.utils.logging import Logger platform_name = platform.system() @@ -971,8 +971,8 @@ def open_file(): def print_banner(host: str = "127.0.0.1", port: int = 5000, debug: bool = False): """Print startup banner with server information""" - from openspace.utils.display import colorize, print_section, print_separator - from openspace.utils.display import print_banner as display_banner + from scion.utils.display import colorize, print_section, print_separator + from scion.utils.display import print_banner as display_banner # STARTUP INFORMATION display_banner("OpenSpace · Local Server") @@ -1003,7 +1003,7 @@ def run_health_check_async(): """Asynchronous running health check""" def _run(): - from openspace.utils.display import colorize + from scion.utils.display import colorize time.sleep(2) @@ -1046,7 +1046,7 @@ def run_server(host: str = "127.0.0.1", port: int = 5000, debug: bool = False): def main(): import argparse - from openspace.config.utils import get_config_value + from scion.config.utils import get_config_value parser = argparse.ArgumentParser(description="OpenSpace Local Server - Desktop Control Server") parser.add_argument("--host", type=str, default="127.0.0.1", help="Server host (default: 127.0.0.1)") diff --git a/openspace/local_server/platform_adapters/__init__.py b/scion/local_server/platform_adapters/__init__.py similarity index 100% rename from openspace/local_server/platform_adapters/__init__.py rename to scion/local_server/platform_adapters/__init__.py diff --git a/openspace/local_server/platform_adapters/linux_adapter.py b/scion/local_server/platform_adapters/linux_adapter.py similarity index 99% rename from openspace/local_server/platform_adapters/linux_adapter.py rename to scion/local_server/platform_adapters/linux_adapter.py index 196bebd..7ceb130 100644 --- a/openspace/local_server/platform_adapters/linux_adapter.py +++ b/scion/local_server/platform_adapters/linux_adapter.py @@ -5,7 +5,7 @@ import pyautogui from PIL import Image -from openspace.utils.logging import Logger +from scion.utils.logging import Logger try: import pyatspi diff --git a/openspace/local_server/platform_adapters/macos_adapter.py b/scion/local_server/platform_adapters/macos_adapter.py similarity index 99% rename from openspace/local_server/platform_adapters/macos_adapter.py rename to scion/local_server/platform_adapters/macos_adapter.py index 3be8edd..eae78dd 100644 --- a/openspace/local_server/platform_adapters/macos_adapter.py +++ b/scion/local_server/platform_adapters/macos_adapter.py @@ -2,7 +2,7 @@ import subprocess from typing import Any, Dict, List, Optional -from openspace.utils.logging import Logger +from scion.utils.logging import Logger try: import AppKit diff --git a/openspace/local_server/platform_adapters/pyxcursor.py b/scion/local_server/platform_adapters/pyxcursor.py similarity index 100% rename from openspace/local_server/platform_adapters/pyxcursor.py rename to scion/local_server/platform_adapters/pyxcursor.py diff --git a/openspace/local_server/platform_adapters/windows_adapter.py b/scion/local_server/platform_adapters/windows_adapter.py similarity index 99% rename from openspace/local_server/platform_adapters/windows_adapter.py rename to scion/local_server/platform_adapters/windows_adapter.py index b7cda23..cdf874f 100644 --- a/openspace/local_server/platform_adapters/windows_adapter.py +++ b/scion/local_server/platform_adapters/windows_adapter.py @@ -5,7 +5,7 @@ from PIL import Image, ImageGrab -from openspace.utils.logging import Logger +from scion.utils.logging import Logger try: import pygetwindow as gw diff --git a/openspace/local_server/requirements.txt b/scion/local_server/requirements.txt similarity index 100% rename from openspace/local_server/requirements.txt rename to scion/local_server/requirements.txt diff --git a/openspace/local_server/run.sh b/scion/local_server/run.sh similarity index 100% rename from openspace/local_server/run.sh rename to scion/local_server/run.sh diff --git a/openspace/local_server/utils/__init__.py b/scion/local_server/utils/__init__.py similarity index 100% rename from openspace/local_server/utils/__init__.py rename to scion/local_server/utils/__init__.py diff --git a/openspace/local_server/utils/accessibility.py b/scion/local_server/utils/accessibility.py similarity index 99% rename from openspace/local_server/utils/accessibility.py rename to scion/local_server/utils/accessibility.py index 81bba1b..f91b6f6 100644 --- a/openspace/local_server/utils/accessibility.py +++ b/scion/local_server/utils/accessibility.py @@ -1,7 +1,7 @@ import platform from typing import Any, Dict, Optional -from openspace.utils.logging import Logger +from scion.utils.logging import Logger logger = Logger.get_logger(__name__) diff --git a/openspace/local_server/utils/screenshot.py b/scion/local_server/utils/screenshot.py similarity index 100% rename from openspace/local_server/utils/screenshot.py rename to scion/local_server/utils/screenshot.py diff --git a/openspace/mcp_server.py b/scion/mcp_server.py similarity index 95% rename from openspace/mcp_server.py rename to scion/mcp_server.py index aa09a5d..6b4b9a8 100644 --- a/openspace/mcp_server.py +++ b/scion/mcp_server.py @@ -7,9 +7,9 @@ upload_skill — Upload a local skill to cloud (pre-saved metadata, bot decides visibility) Usage: - python -m openspace.mcp_server # stdio (default) - python -m openspace.mcp_server --transport sse # SSE on port 8080 - python -m openspace.mcp_server --port 9090 # SSE on custom port + python -m scion.mcp_server # stdio (default) + python -m scion.mcp_server --transport sse # SSE on port 8080 + python -m scion.mcp_server --port 9090 # SSE on custom port Environment variables: see ``openspace/host_detection/`` and ``openspace/cloud/auth.py``. """ @@ -103,7 +103,7 @@ def __getattr__(self, name): format="%(asctime)s - %(name)s - %(levelname)s - %(message)s", handlers=[_log_file_handler], ) -logger = logging.getLogger("openspace.mcp_server") +logger = logging.getLogger("scion.mcp_server") def _cleanup_file_handles(): @@ -151,8 +151,8 @@ async def _get_openspace(): return _openspace_instance logger.info("Initializing OpenSpace engine ...") - from openspace.host_detection import build_grounding_config_path, build_llm_kwargs - from openspace.tool_layer import OpenSpace, OpenSpaceConfig + from scion.host_detection import build_grounding_config_path, build_llm_kwargs + from scion.tool_layer import OpenSpace, OpenSpaceConfig env_model = os.environ.get("OPENSPACE_MODEL", "") workspace = os.environ.get("OPENSPACE_WORKSPACE") @@ -204,7 +204,7 @@ def _get_store(): if internal and not internal._closed: return internal if _standalone_store is None or _standalone_store._closed: - from openspace.skill_engine import SkillStore + from scion.skill_engine import SkillStore _standalone_store = SkillStore() return _standalone_store @@ -226,8 +226,8 @@ def _is_auto_import_enabled() -> bool: def _get_cloud_client(): """Get a OpenSpaceClient instance (raises CloudError if not configured).""" - from openspace.cloud.auth import get_openspace_auth - from openspace.cloud.client import OpenSpaceClient + from scion.cloud.auth import get_openspace_auth + from scion.cloud.client import OpenSpaceClient auth_headers, api_base = get_openspace_auth() return OpenSpaceClient(auth_headers, api_base) @@ -244,7 +244,7 @@ def _write_upload_meta(skill_dir: Path, info: Dict[str, Any]) -> None: "origin": info.get("origin", "imported"), "parent_skill_ids": info.get("parent_skill_ids", []), "change_summary": info.get("change_summary", ""), - "created_by": info.get("created_by", "openspace"), + "created_by": info.get("created_by", "scion"), "tags": info.get("tags", []), } meta_path = skill_dir / _UPLOAD_META_FILENAME @@ -354,8 +354,8 @@ async def _cloud_search_and_import(task: str, limit: int = 8) -> List[Dict[str, logger.debug("Cloud auto-import is disabled (auto_import_enabled=False)") return [] try: - from openspace.cloud.embedding import generate_embedding, resolve_embedding_api - from openspace.cloud.search import ( + from scion.cloud.embedding import generate_embedding, resolve_embedding_api + from scion.cloud.search import ( SkillSearchEngine, build_cloud_candidates, ) @@ -518,7 +518,7 @@ def _json_ok(data: Any) -> str: def _json_error(error_msg: str, *, error_code: str = "VALIDATION_ERROR") -> str: """Return a structured error for validation / not-found cases.""" - from openspace.errors import safe_error_response + from scion.errors import safe_error_response return safe_error_response(error_code, error_msg) @@ -599,7 +599,7 @@ async def execute_task( return _json_ok(formatted) except Exception as e: - from openspace.errors import EXECUTION_ERROR, handle_mcp_exception + from scion.errors import EXECUTION_ERROR, handle_mcp_exception return handle_mcp_exception(e, tool_name="execute_task", error_code=EXECUTION_ERROR) @@ -632,7 +632,7 @@ async def search_skills( auto_import: Auto-download top public cloud skills (default: True). """ try: - from openspace.cloud.search import hybrid_search_skills + from scion.cloud.search import hybrid_search_skills q = query.strip() if not q: @@ -702,7 +702,7 @@ async def search_skills( return _json_ok(output) except Exception as e: - from openspace.errors import EXECUTION_ERROR, handle_mcp_exception + from scion.errors import EXECUTION_ERROR, handle_mcp_exception return handle_mcp_exception(e, tool_name="search_skills", error_code=EXECUTION_ERROR) @@ -737,8 +737,8 @@ async def fix_skill( "Add retry logic for HTTP 429 rate limit errors". """ try: - from openspace.skill_engine.evolver import EvolutionContext, EvolutionTrigger - from openspace.skill_engine.types import EvolutionSuggestion, EvolutionType + from scion.skill_engine.evolver import EvolutionContext, EvolutionTrigger + from scion.skill_engine.types import EvolutionSuggestion, EvolutionType if not direction: return _json_error("direction is required — describe what to fix.") @@ -809,7 +809,7 @@ async def fix_skill( "origin": new_record.lineage.origin.value, "parent_skill_ids": new_record.lineage.parent_skill_ids, "change_summary": new_record.lineage.change_summary, - "created_by": new_record.lineage.created_by or "openspace", + "created_by": new_record.lineage.created_by or "scion", "tags": new_record.tags, }, ) @@ -828,7 +828,7 @@ async def fix_skill( ) except Exception as e: - from openspace.errors import EXECUTION_ERROR, handle_mcp_exception + from scion.errors import EXECUTION_ERROR, handle_mcp_exception return handle_mcp_exception(e, tool_name="fix_skill", error_code=EXECUTION_ERROR) @@ -900,7 +900,7 @@ async def upload_skill( return _json_ok(result) except Exception as e: - from openspace.errors import EXECUTION_ERROR, handle_mcp_exception + from scion.errors import EXECUTION_ERROR, handle_mcp_exception return handle_mcp_exception(e, tool_name="upload_skill", error_code=EXECUTION_ERROR) @@ -918,13 +918,13 @@ def run_mcp_server() -> None: import uvicorn - from openspace.auth.bearer import ( + from scion.auth.bearer import ( BEARER_TOKEN_ENV, BearerTokenMiddleware, get_bearer_token, validate_token_strength, ) - from openspace.auth.rate_limit import RateLimitMiddleware + from scion.auth.rate_limit import RateLimitMiddleware parser = argparse.ArgumentParser(description="OpenSpace MCP Server") parser.add_argument( diff --git a/openspace/platforms/__init__.py b/scion/platforms/__init__.py similarity index 100% rename from openspace/platforms/__init__.py rename to scion/platforms/__init__.py diff --git a/openspace/platforms/config.py b/scion/platforms/config.py similarity index 98% rename from openspace/platforms/config.py rename to scion/platforms/config.py index 3d57a76..b2647ed 100644 --- a/openspace/platforms/config.py +++ b/scion/platforms/config.py @@ -2,7 +2,7 @@ import os from typing import Any, Dict -from openspace.utils.logging import Logger +from scion.utils.logging import Logger logger = Logger.get_logger(__name__) diff --git a/openspace/platforms/recording.py b/scion/platforms/recording.py similarity index 98% rename from openspace/platforms/recording.py rename to scion/platforms/recording.py index e7d11c3..b1fc69a 100644 --- a/openspace/platforms/recording.py +++ b/scion/platforms/recording.py @@ -2,7 +2,7 @@ import aiohttp -from openspace.utils.logging import Logger +from scion.utils.logging import Logger from .config import get_client_base_url @@ -152,7 +152,7 @@ def __init__( # Load output_path from config if not provided if output_path is None: try: - from openspace.config import get_config + from scion.config import get_config config = get_config() if config.recording.screen_recording_path: diff --git a/openspace/platforms/screenshot.py b/scion/platforms/screenshot.py similarity index 99% rename from openspace/platforms/screenshot.py rename to scion/platforms/screenshot.py index 49f3644..57ffbdb 100644 --- a/openspace/platforms/screenshot.py +++ b/scion/platforms/screenshot.py @@ -13,7 +13,7 @@ import aiohttp -from openspace.utils.logging import Logger +from scion.utils.logging import Logger from .config import get_client_base_url diff --git a/openspace/platforms/system_info.py b/scion/platforms/system_info.py similarity index 99% rename from openspace/platforms/system_info.py rename to scion/platforms/system_info.py index de9cddb..4378e36 100644 --- a/openspace/platforms/system_info.py +++ b/scion/platforms/system_info.py @@ -2,7 +2,7 @@ import aiohttp -from openspace.utils.logging import Logger +from scion.utils.logging import Logger from .config import get_client_base_url diff --git a/scion/prompts/__init__.py b/scion/prompts/__init__.py new file mode 100644 index 0000000..2527216 --- /dev/null +++ b/scion/prompts/__init__.py @@ -0,0 +1,4 @@ +from scion.prompts.grounding_agent_prompts import GroundingAgentPrompts +from scion.prompts.skill_engine_prompts import SkillEnginePrompts + +__all__ = ["GroundingAgentPrompts", "SkillEnginePrompts"] diff --git a/openspace/prompts/grounding_agent_prompts.py b/scion/prompts/grounding_agent_prompts.py similarity index 100% rename from openspace/prompts/grounding_agent_prompts.py rename to scion/prompts/grounding_agent_prompts.py diff --git a/openspace/prompts/skill_engine_prompts.py b/scion/prompts/skill_engine_prompts.py similarity index 100% rename from openspace/prompts/skill_engine_prompts.py rename to scion/prompts/skill_engine_prompts.py diff --git a/openspace/recording/__init__.py b/scion/recording/__init__.py similarity index 100% rename from openspace/recording/__init__.py rename to scion/recording/__init__.py diff --git a/openspace/recording/action_recorder.py b/scion/recording/action_recorder.py similarity index 99% rename from openspace/recording/action_recorder.py rename to scion/recording/action_recorder.py index 7c59392..e950600 100644 --- a/openspace/recording/action_recorder.py +++ b/scion/recording/action_recorder.py @@ -10,7 +10,7 @@ from pathlib import Path from typing import Any, Dict, Optional -from openspace.utils.logging import Logger +from scion.utils.logging import Logger logger = Logger.get_logger(__name__) diff --git a/openspace/recording/manager.py b/scion/recording/manager.py similarity index 99% rename from openspace/recording/manager.py rename to scion/recording/manager.py index d3ea0c3..7a16617 100644 --- a/openspace/recording/manager.py +++ b/scion/recording/manager.py @@ -5,7 +5,7 @@ from pathlib import Path from typing import Any, Dict, List, Optional -from openspace.utils.logging import Logger +from scion.utils.logging import Logger from .action_recorder import ActionRecorder from .recorder import TrajectoryRecorder @@ -509,7 +509,7 @@ async def start(self, task_id: Optional[str] = None): # create video client (internal management) if self.enable_video: - from openspace.platforms import RecordingClient + from scion.platforms import RecordingClient self._recording_client = RecordingClient(base_url=self.server_url) success = await self._recording_client.start_recording() @@ -520,7 +520,7 @@ async def start(self, task_id: Optional[str] = None): # create screenshot client (internal management) if self.enable_screenshot: - from openspace.platforms import ScreenshotClient + from scion.platforms import ScreenshotClient self._screenshot_client = ScreenshotClient(base_url=self.server_url) logger.debug("Screenshot client ready") @@ -550,7 +550,7 @@ async def start(self, task_id: Optional[str] = None): async def _check_server_availability(self): """Check if local server is available""" try: - from openspace.platforms import SystemInfoClient + from scion.platforms import SystemInfoClient # Use context manager to ensure aiohttp session is closed, avoiding warning of unclosed session async with SystemInfoClient(base_url=self.server_url) as client: diff --git a/openspace/recording/recorder.py b/scion/recording/recorder.py similarity index 98% rename from openspace/recording/recorder.py rename to scion/recording/recorder.py index e2b4154..dd8940b 100644 --- a/openspace/recording/recorder.py +++ b/scion/recording/recorder.py @@ -3,7 +3,7 @@ from pathlib import Path from typing import Any, Dict, List, Optional -from openspace.utils.logging import Logger +from scion.utils.logging import Logger logger = Logger.get_logger(__name__) @@ -147,7 +147,7 @@ async def record_step( async def _capture_screenshot(self) -> Optional[bytes]: """Capture screenshot automatically through platforms.ScreenshotClient""" try: - from openspace.platforms import ScreenshotClient + from scion.platforms import ScreenshotClient # Lazy initialization screenshot client if not hasattr(self, "_screenshot_client"): @@ -202,7 +202,7 @@ async def start_video_recording(self): return try: - from openspace.recording.video import VideoRecorder + from scion.recording.video import VideoRecorder video_path = self.trajectory_dir / "recording.mp4" self._video_recorder = VideoRecorder(str(video_path), base_url=self.server_url) diff --git a/openspace/recording/utils.py b/scion/recording/utils.py similarity index 99% rename from openspace/recording/utils.py rename to scion/recording/utils.py index 20d3e3a..a496a2c 100644 --- a/openspace/recording/utils.py +++ b/scion/recording/utils.py @@ -3,7 +3,7 @@ from pathlib import Path from typing import Any, Dict, List, Optional, Tuple -from openspace.utils.logging import Logger +from scion.utils.logging import Logger logger = Logger.get_logger(__name__) @@ -85,7 +85,7 @@ def _format_detailed(trajectory: List[Dict[str, Any]]) -> str: parameters = step.get("parameters", {}) result = step.get("result", {}) - from openspace.utils.display import Box, BoxStyle + from scion.utils.display import Box, BoxStyle box = Box(width=66, style=BoxStyle.ROUNDED, color="bl") lines.append("") diff --git a/openspace/recording/video.py b/scion/recording/video.py similarity index 96% rename from openspace/recording/video.py rename to scion/recording/video.py index bf787f0..07288df 100644 --- a/openspace/recording/video.py +++ b/scion/recording/video.py @@ -8,8 +8,8 @@ from pathlib import Path from typing import Optional -from openspace.platforms import RecordingClient -from openspace.utils.logging import Logger +from scion.platforms import RecordingClient +from scion.utils.logging import Logger logger = Logger.get_logger(__name__) diff --git a/openspace/recording/viewer.py b/scion/recording/viewer.py similarity index 98% rename from openspace/recording/viewer.py rename to scion/recording/viewer.py index 5a21a49..964579f 100644 --- a/openspace/recording/viewer.py +++ b/scion/recording/viewer.py @@ -7,7 +7,7 @@ from pathlib import Path from typing import Optional -from openspace.utils.logging import Logger +from scion.utils.logging import Logger from .action_recorder import analyze_agent_actions, format_agent_actions, load_agent_actions from .utils import generate_summary_report, load_recording_session @@ -331,7 +331,7 @@ def compare_recordings(recording_dir1: str, recording_dir2: str) -> str: import sys if len(sys.argv) < 2: - print("Usage: python -m openspace.recording.viewer ") + print("Usage: python -m scion.recording.viewer ") sys.exit(1) recording_dir = sys.argv[1] diff --git a/openspace/sandbox/__init__.py b/scion/sandbox/__init__.py similarity index 100% rename from openspace/sandbox/__init__.py rename to scion/sandbox/__init__.py diff --git a/openspace/sandbox/fs_broker.py b/scion/sandbox/fs_broker.py similarity index 99% rename from openspace/sandbox/fs_broker.py rename to scion/sandbox/fs_broker.py index f9b6639..838871c 100644 --- a/openspace/sandbox/fs_broker.py +++ b/scion/sandbox/fs_broker.py @@ -20,7 +20,7 @@ from pathlib import Path, PurePosixPath from typing import Optional, Union -from openspace.sandbox.leases import FilesystemCapability +from scion.sandbox.leases import FilesystemCapability # --------------------------------------------------------------------------- # Constants diff --git a/openspace/sandbox/leases.py b/scion/sandbox/leases.py similarity index 99% rename from openspace/sandbox/leases.py rename to scion/sandbox/leases.py index a691443..43de065 100644 --- a/openspace/sandbox/leases.py +++ b/scion/sandbox/leases.py @@ -19,7 +19,7 @@ from pydantic import BaseModel, Field, field_validator, model_validator -from openspace.domain.types import CapabilityLease, SandboxPolicy +from scion.domain.types import CapabilityLease, SandboxPolicy # --------------------------------------------------------------------------- # #84 — Capability Lease YAML Schema diff --git a/openspace/sandbox/net_proxy.py b/scion/sandbox/net_proxy.py similarity index 99% rename from openspace/sandbox/net_proxy.py rename to scion/sandbox/net_proxy.py index e7acc23..8cab253 100644 --- a/openspace/sandbox/net_proxy.py +++ b/scion/sandbox/net_proxy.py @@ -18,7 +18,7 @@ from dataclasses import dataclass, field from typing import Optional -from openspace.sandbox.leases import NetworkCapability +from scion.sandbox.leases import NetworkCapability # --------------------------------------------------------------------------- # Known DNS rebinding services that resolve to arbitrary IPs. diff --git a/openspace/sandbox/process_broker.py b/scion/sandbox/process_broker.py similarity index 99% rename from openspace/sandbox/process_broker.py rename to scion/sandbox/process_broker.py index 38ada99..c1c714d 100644 --- a/openspace/sandbox/process_broker.py +++ b/scion/sandbox/process_broker.py @@ -18,7 +18,7 @@ from dataclasses import dataclass, field from pathlib import PurePosixPath, PureWindowsPath -from openspace.sandbox.leases import REQUIRED_BLOCKED_COMMANDS, ProcessCapability +from scion.sandbox.leases import REQUIRED_BLOCKED_COMMANDS, ProcessCapability # --------------------------------------------------------------------------- # Constants diff --git a/openspace/secret/__init__.py b/scion/secret/__init__.py similarity index 100% rename from openspace/secret/__init__.py rename to scion/secret/__init__.py diff --git a/openspace/secret/broker.py b/scion/secret/broker.py similarity index 99% rename from openspace/secret/broker.py rename to scion/secret/broker.py index 552049e..1524161 100644 --- a/openspace/secret/broker.py +++ b/scion/secret/broker.py @@ -35,7 +35,7 @@ from enum import Enum from typing import Optional -from openspace.sandbox.leases import SecretCapability +from scion.sandbox.leases import SecretCapability # --------------------------------------------------------------------------- # Secret Scope Model diff --git a/openspace/security/__init__.py b/scion/security/__init__.py similarity index 97% rename from openspace/security/__init__.py rename to scion/security/__init__.py index d837c4f..ae5a821 100644 --- a/openspace/security/__init__.py +++ b/scion/security/__init__.py @@ -13,7 +13,7 @@ from typing import List, Tuple -from openspace.utils.logging import Logger +from scion.utils.logging import Logger from .ast_scanner import Finding, Severity, scan_code, scan_file # noqa: F401 diff --git a/openspace/security/ast_scanner.py b/scion/security/ast_scanner.py similarity index 99% rename from openspace/security/ast_scanner.py rename to scion/security/ast_scanner.py index 0dc315b..08d16f6 100644 --- a/openspace/security/ast_scanner.py +++ b/scion/security/ast_scanner.py @@ -18,7 +18,7 @@ from pathlib import Path from typing import Any, Dict, List, Sequence -from openspace.utils.logging import Logger +from scion.utils.logging import Logger logger = Logger.get_logger(__name__) diff --git a/openspace/security/blocklist.yml b/scion/security/blocklist.yml similarity index 100% rename from openspace/security/blocklist.yml rename to scion/security/blocklist.yml diff --git a/openspace/security/env_filter.py b/scion/security/env_filter.py similarity index 100% rename from openspace/security/env_filter.py rename to scion/security/env_filter.py diff --git a/openspace/skill_engine/__init__.py b/scion/skill_engine/__init__.py similarity index 100% rename from openspace/skill_engine/__init__.py rename to scion/skill_engine/__init__.py diff --git a/openspace/skill_engine/analysis_store.py b/scion/skill_engine/analysis_store.py similarity index 99% rename from openspace/skill_engine/analysis_store.py rename to scion/skill_engine/analysis_store.py index 306dd97..9e18634 100644 --- a/openspace/skill_engine/analysis_store.py +++ b/scion/skill_engine/analysis_store.py @@ -24,7 +24,7 @@ from pathlib import Path from typing import Any, Callable, Dict, Generator, List, Optional -from openspace.utils.logging import Logger +from scion.utils.logging import Logger from .migration_manager import MigrationManager diff --git a/openspace/skill_engine/analyzer.py b/scion/skill_engine/analyzer.py similarity index 99% rename from openspace/skill_engine/analyzer.py rename to scion/skill_engine/analyzer.py index cdb123e..39f044d 100644 --- a/openspace/skill_engine/analyzer.py +++ b/scion/skill_engine/analyzer.py @@ -20,9 +20,9 @@ from pathlib import Path from typing import TYPE_CHECKING, Any, Dict, List, Optional -from openspace.grounding.core.tool import BaseTool -from openspace.prompts import SkillEnginePrompts -from openspace.utils.logging import Logger +from scion.grounding.core.tool import BaseTool +from scion.prompts import SkillEnginePrompts +from scion.utils.logging import Logger from .conversation_formatter import format_conversations from .store import SkillStore @@ -35,8 +35,8 @@ ) if TYPE_CHECKING: - from openspace.grounding.core.quality import ToolQualityManager - from openspace.llm import LLMClient + from scion.grounding.core.quality import ToolQualityManager + from scion.llm import LLMClient from .registry import SkillRegistry @@ -691,7 +691,7 @@ async def _run_analysis_loop( ``RecordingManager`` (agent_name="ExecutionAnalyzer") so the full analysis dialogue is preserved alongside the grounding trace. """ - from openspace.recording import RecordingManager + from scion.recording import RecordingManager model = self._model or self._llm_client.model analysis_tools: List[BaseTool] = list(available_tools or []) diff --git a/openspace/skill_engine/conversation_formatter.py b/scion/skill_engine/conversation_formatter.py similarity index 100% rename from openspace/skill_engine/conversation_formatter.py rename to scion/skill_engine/conversation_formatter.py diff --git a/openspace/skill_engine/evolver.py b/scion/skill_engine/evolver.py similarity index 99% rename from openspace/skill_engine/evolver.py rename to scion/skill_engine/evolver.py index da07736..f31636d 100644 --- a/openspace/skill_engine/evolver.py +++ b/scion/skill_engine/evolver.py @@ -27,8 +27,8 @@ from pathlib import Path from typing import TYPE_CHECKING, Any, Dict, List, Optional, Set -from openspace.prompts import SkillEnginePrompts -from openspace.utils.logging import Logger +from scion.prompts import SkillEnginePrompts +from scion.utils.logging import Logger from .patch import ( SKILL_FILENAME, @@ -70,9 +70,9 @@ ) if TYPE_CHECKING: - from openspace.grounding.core.quality.types import ToolQualityRecord - from openspace.grounding.core.tool import BaseTool - from openspace.llm import LLMClient + from scion.grounding.core.quality.types import ToolQualityRecord + from scion.grounding.core.tool import BaseTool + from scion.llm import LLMClient from .registry import SkillRegistry @@ -578,7 +578,7 @@ async def _llm_confirm_evolution( The confirmation prompt and response are recorded to ``conversations.jsonl`` under agent_name="SkillEvolver.confirm". """ - from openspace.recording import RecordingManager + from scion.recording import RecordingManager analysis_ctx = self._format_analysis_context(recent_analyses) @@ -1027,7 +1027,7 @@ async def _run_evolution_loop( ``RecordingManager`` (agent_name="SkillEvolver") so the full evolution dialogue is preserved for debugging and replay. """ - from openspace.recording import RecordingManager + from scion.recording import RecordingManager model = self._model or self._llm_client.model @@ -1218,7 +1218,7 @@ async def _apply_with_retry( prompt: Original prompt (for retry context). cleanup_on_retry: Directory to remove before retrying (for derive/create). """ - from openspace.recording import RecordingManager + from scion.recording import RecordingManager current_content = initial_content msg_history: List[Dict[str, Any]] = [ diff --git a/openspace/skill_engine/fuzzy_match.py b/scion/skill_engine/fuzzy_match.py similarity index 99% rename from openspace/skill_engine/fuzzy_match.py rename to scion/skill_engine/fuzzy_match.py index 9ecf4e1..15c3550 100644 --- a/openspace/skill_engine/fuzzy_match.py +++ b/scion/skill_engine/fuzzy_match.py @@ -14,7 +14,7 @@ import re from typing import Generator, List, Optional, Tuple -from openspace.utils.logging import Logger +from scion.utils.logging import Logger logger = Logger.get_logger(__name__) diff --git a/openspace/skill_engine/lineage_tracker.py b/scion/skill_engine/lineage_tracker.py similarity index 99% rename from openspace/skill_engine/lineage_tracker.py rename to scion/skill_engine/lineage_tracker.py index c36cede..98ac377 100644 --- a/openspace/skill_engine/lineage_tracker.py +++ b/scion/skill_engine/lineage_tracker.py @@ -24,7 +24,7 @@ from pathlib import Path from typing import Any, Dict, Generator, List, Optional -from openspace.utils.logging import Logger +from scion.utils.logging import Logger from .migration_manager import MigrationManager from .types import ( diff --git a/openspace/skill_engine/migration_manager.py b/scion/skill_engine/migration_manager.py similarity index 99% rename from openspace/skill_engine/migration_manager.py rename to scion/skill_engine/migration_manager.py index fd9ab78..8f130ea 100644 --- a/openspace/skill_engine/migration_manager.py +++ b/scion/skill_engine/migration_manager.py @@ -30,7 +30,7 @@ from pathlib import Path from typing import Generator, Optional -from openspace.utils.logging import Logger +from scion.utils.logging import Logger logger = Logger.get_logger(__name__) diff --git a/openspace/skill_engine/patch.py b/scion/skill_engine/patch.py similarity index 99% rename from openspace/skill_engine/patch.py rename to scion/skill_engine/patch.py index 5b48b64..bc34070 100644 --- a/openspace/skill_engine/patch.py +++ b/scion/skill_engine/patch.py @@ -26,7 +26,7 @@ from pathlib import Path from typing import Callable, Dict, List, Optional, Tuple, Union -from openspace.utils.logging import Logger +from scion.utils.logging import Logger from .fuzzy_match import fuzzy_find_match from .skill_utils import normalize_frontmatter diff --git a/openspace/skill_engine/registry.py b/scion/skill_engine/registry.py similarity index 99% rename from openspace/skill_engine/registry.py rename to scion/skill_engine/registry.py index 3d9621f..be30478 100644 --- a/openspace/skill_engine/registry.py +++ b/scion/skill_engine/registry.py @@ -28,13 +28,13 @@ from pathlib import Path from typing import TYPE_CHECKING, Any, Dict, List, Optional -from openspace.utils.logging import Logger +from scion.utils.logging import Logger from .skill_ranker import PREFILTER_THRESHOLD, SkillCandidate, SkillRanker from .skill_utils import check_skill_safety, is_skill_safe, parse_frontmatter, strip_frontmatter if TYPE_CHECKING: - from openspace.llm import LLMClient + from scion.llm import LLMClient logger = Logger.get_logger(__name__) diff --git a/openspace/skill_engine/retrieve_tool.py b/scion/skill_engine/retrieve_tool.py similarity index 90% rename from openspace/skill_engine/retrieve_tool.py rename to scion/skill_engine/retrieve_tool.py index 3e42ec3..ad7b9eb 100644 --- a/openspace/skill_engine/retrieve_tool.py +++ b/scion/skill_engine/retrieve_tool.py @@ -11,14 +11,14 @@ from typing import TYPE_CHECKING, Any, Dict, List, Optional -from openspace.grounding.core.tool.local_tool import LocalTool -from openspace.grounding.core.types import BackendType -from openspace.utils.logging import Logger +from scion.grounding.core.tool.local_tool import LocalTool +from scion.grounding.core.types import BackendType +from scion.utils.logging import Logger if TYPE_CHECKING: - from openspace.llm import LLMClient - from openspace.skill_engine import SkillRegistry - from openspace.skill_engine.store import SkillStore + from scion.llm import LLMClient + from scion.skill_engine import SkillRegistry + from scion.skill_engine.store import SkillStore logger = Logger.get_logger(__name__) @@ -85,7 +85,7 @@ async def _arun(self, query: str) -> str: logger.info(f"retrieve_skill plan: {plan}") else: # Fallback: BM25+embedding only (no LLM available) - from openspace.cloud.search import hybrid_search_skills + from scion.cloud.search import hybrid_search_skills results = await hybrid_search_skills( query=query, diff --git a/openspace/skill_engine/skill_ranker.py b/scion/skill_engine/skill_ranker.py similarity index 97% rename from openspace/skill_engine/skill_ranker.py rename to scion/skill_engine/skill_ranker.py index 59ff2e1..937e5ff 100644 --- a/openspace/skill_engine/skill_ranker.py +++ b/scion/skill_engine/skill_ranker.py @@ -27,7 +27,7 @@ from pathlib import Path from typing import Any, Dict, List, Optional -from openspace.utils.logging import Logger +from scion.utils.logging import Logger logger = Logger.get_logger(__name__) @@ -87,7 +87,7 @@ def __init__( if cache_dir is None: try: - from openspace.config.constants import PROJECT_ROOT + from scion.config.constants import PROJECT_ROOT cache_dir = PROJECT_ROOT / ".openspace" / "skill_embedding_cache" except Exception: @@ -243,7 +243,7 @@ def _bm25_rank( @staticmethod def _get_openai_api_key() -> Optional[str]: """Resolve OpenAI-compatible API key for embedding requests.""" - from openspace.cloud.embedding import resolve_embedding_api + from scion.cloud.embedding import resolve_embedding_api api_key, _ = resolve_embedding_api() return api_key @@ -311,9 +311,9 @@ def _generate_embedding( """Generate embedding via OpenAI-compatible API (text-embedding-3-small). Delegates credential / base-URL resolution to - :func:`openspace.cloud.embedding.resolve_embedding_api`. + :func:`scion.cloud.embedding.resolve_embedding_api`. """ - from openspace.cloud.embedding import resolve_embedding_api + from scion.cloud.embedding import resolve_embedding_api resolved_key, base_url = resolve_embedding_api() if not api_key: diff --git a/openspace/skill_engine/skill_repository.py b/scion/skill_engine/skill_repository.py similarity index 99% rename from openspace/skill_engine/skill_repository.py rename to scion/skill_engine/skill_repository.py index d932cfa..bdba6d2 100644 --- a/openspace/skill_engine/skill_repository.py +++ b/scion/skill_engine/skill_repository.py @@ -22,7 +22,7 @@ from pathlib import Path from typing import Any, Dict, Generator, List, Optional -from openspace.utils.logging import Logger +from scion.utils.logging import Logger from .migration_manager import MigrationManager from .patch import collect_skill_snapshot, compute_unified_diff diff --git a/openspace/skill_engine/skill_utils.py b/scion/skill_engine/skill_utils.py similarity index 99% rename from openspace/skill_engine/skill_utils.py rename to scion/skill_engine/skill_utils.py index a87bcc4..a3722bb 100644 --- a/openspace/skill_engine/skill_utils.py +++ b/scion/skill_engine/skill_utils.py @@ -14,7 +14,7 @@ from pathlib import Path from typing import Any, Dict, List, Optional -from openspace.utils.logging import Logger +from scion.utils.logging import Logger logger = Logger.get_logger(__name__) diff --git a/openspace/skill_engine/store.py b/scion/skill_engine/store.py similarity index 99% rename from openspace/skill_engine/store.py rename to scion/skill_engine/store.py index 16f59ba..9e4e226 100644 --- a/openspace/skill_engine/store.py +++ b/scion/skill_engine/store.py @@ -9,7 +9,7 @@ ├── AnalysisStore — Execution analysis persistence (analysis_store.py) └── TagSearch — Tag indexing & search operations (tag_search.py) -Storage location: /.openspace/openspace.db +Storage location: /.openspace/scion.db Tables: skill_records — SkillRecord main table @@ -40,8 +40,8 @@ from pathlib import Path from typing import Any, Dict, Generator, List, Optional -from openspace.config.constants import PROJECT_ROOT -from openspace.utils.logging import Logger +from scion.config.constants import PROJECT_ROOT +from scion.utils.logging import Logger from .analysis_store import AnalysisStore from .lineage_tracker import LineageTracker @@ -120,7 +120,7 @@ def __init__(self, db_path: Optional[Path] = None) -> None: if db_path is None: db_dir = PROJECT_ROOT / ".openspace" db_dir.mkdir(parents=True, exist_ok=True) - db_path = db_dir / "openspace.db" + db_path = db_dir / "scion.db" self._db_path = Path(db_path) self._mu = threading.Lock() diff --git a/openspace/skill_engine/tag_search.py b/scion/skill_engine/tag_search.py similarity index 99% rename from openspace/skill_engine/tag_search.py rename to scion/skill_engine/tag_search.py index f7db667..68399b0 100644 --- a/openspace/skill_engine/tag_search.py +++ b/scion/skill_engine/tag_search.py @@ -20,7 +20,7 @@ from pathlib import Path from typing import Any, Callable, Dict, Generator, List, Optional -from openspace.utils.logging import Logger +from scion.utils.logging import Logger from .migration_manager import MigrationManager from .types import SkillCategory, SkillVisibility diff --git a/openspace/skill_engine/types.py b/scion/skill_engine/types.py similarity index 100% rename from openspace/skill_engine/types.py rename to scion/skill_engine/types.py diff --git a/openspace/skills/README.md b/scion/skills/README.md similarity index 100% rename from openspace/skills/README.md rename to scion/skills/README.md diff --git a/openspace/tool_layer.py b/scion/tool_layer.py similarity index 98% rename from openspace/tool_layer.py rename to scion/tool_layer.py index 9f4d15d..36f16c2 100644 --- a/openspace/tool_layer.py +++ b/scion/tool_layer.py @@ -7,16 +7,16 @@ from pathlib import Path from typing import Any, Dict, List, Optional -from openspace.agents import GroundingAgent -from openspace.app.container import AppContainer -from openspace.config import get_config, load_config -from openspace.config.loader import get_agent_config -from openspace.grounding.core.grounding_client import GroundingClient -from openspace.llm import LLMClient -from openspace.recording import RecordingManager -from openspace.skill_engine import ExecutionAnalyzer, SkillRegistry, SkillStore -from openspace.skill_engine.evolver import SkillEvolver -from openspace.utils.logging import Logger +from scion.agents import GroundingAgent +from scion.app.container import AppContainer +from scion.config import get_config, load_config +from scion.config.loader import get_agent_config +from scion.grounding.core.grounding_client import GroundingClient +from scion.llm import LLMClient +from scion.recording import RecordingManager +from scion.skill_engine import ExecutionAnalyzer, SkillRegistry, SkillStore +from scion.skill_engine.evolver import SkillEvolver +from scion.utils.logging import Logger logger = Logger.get_logger(__name__) @@ -217,8 +217,8 @@ async def initialize(self) -> None: # If custom config is provided, merge it with default configs # load_config supports multiple files and deep merges them (later files override earlier ones) if self.config.grounding_config_path: - from openspace.config.constants import CONFIG_GROUNDING, CONFIG_SECURITY - from openspace.config.loader import CONFIG_DIR + from scion.config.constants import CONFIG_GROUNDING, CONFIG_SECURITY + from scion.config.loader import CONFIG_DIR # Load default configs + custom config (custom values will override defaults) grounding_config = load_config( @@ -485,7 +485,7 @@ async def execute( # correct task workspace instead of the global CWD. resolved_ws = execution_context["workspace_dir"] try: - from openspace.grounding.core.types import BackendType as _BT + from scion.grounding.core.types import BackendType as _BT shell_prov = self._grounding_client._registry.get(_BT.SHELL) for sess in shell_prov._sessions.values(): diff --git a/openspace/utils/cli_display.py b/scion/utils/cli_display.py similarity index 98% rename from openspace/utils/cli_display.py rename to scion/utils/cli_display.py index d4552d2..28a7b9d 100644 --- a/openspace/utils/cli_display.py +++ b/scion/utils/cli_display.py @@ -1,7 +1,7 @@ """CLI Display utilities for OpenSpace startup and interaction""" -from openspace.tool_layer import OpenSpaceConfig -from openspace.utils.display import Box, BoxStyle, colorize +from scion.tool_layer import OpenSpaceConfig +from scion.utils.display import Box, BoxStyle, colorize class CLIDisplay: diff --git a/openspace/utils/display.py b/scion/utils/display.py similarity index 100% rename from openspace/utils/display.py rename to scion/utils/display.py diff --git a/openspace/utils/logging.py b/scion/utils/logging.py similarity index 99% rename from openspace/utils/logging.py rename to scion/utils/logging.py index 70c07ea..458ebc1 100644 --- a/openspace/utils/logging.py +++ b/scion/utils/logging.py @@ -79,7 +79,7 @@ class Logger: 3. Dynamically adapts log levels according to ``OPENSPACE_DEBUG``. """ - _ROOT_NAME = "openspace" # Package root name + _ROOT_NAME = "scion" # Package root name # Standard format: time with milliseconds | level | file:line number | message _LOG_FORMAT = "%(asctime)s.%(msecs)03d [%(levelname)-8s] %(filename)s:%(lineno)d - %(message)s" @@ -95,7 +95,7 @@ def _get_default_log_file() -> str: - logs//openspace_2025-10-24_15-30-00.log """ # Get the name of the main script - script_name = "openspace" # Default name + script_name = "scion" # Default name try: import __main__ diff --git a/openspace/utils/telemetry/__init__.py b/scion/utils/telemetry/__init__.py similarity index 100% rename from openspace/utils/telemetry/__init__.py rename to scion/utils/telemetry/__init__.py diff --git a/openspace/utils/telemetry/events.py b/scion/utils/telemetry/events.py similarity index 100% rename from openspace/utils/telemetry/events.py rename to scion/utils/telemetry/events.py diff --git a/openspace/utils/telemetry/telemetry.py b/scion/utils/telemetry/telemetry.py similarity index 100% rename from openspace/utils/telemetry/telemetry.py rename to scion/utils/telemetry/telemetry.py diff --git a/openspace/utils/telemetry/utils.py b/scion/utils/telemetry/utils.py similarity index 100% rename from openspace/utils/telemetry/utils.py rename to scion/utils/telemetry/utils.py diff --git a/openspace/utils/ui.py b/scion/utils/ui.py similarity index 99% rename from openspace/utils/ui.py rename to scion/utils/ui.py index a126977..257c65c 100644 --- a/openspace/utils/ui.py +++ b/scion/utils/ui.py @@ -14,7 +14,7 @@ from enum import Enum from typing import Any, Dict, List, Optional, Tuple -from openspace.utils.display import Box, BoxStyle, colorize +from scion.utils.display import Box, BoxStyle, colorize class AgentStatus(Enum): diff --git a/openspace/utils/ui_integration.py b/scion/utils/ui_integration.py similarity index 98% rename from openspace/utils/ui_integration.py rename to scion/utils/ui_integration.py index d95d179..9a768c3 100644 --- a/openspace/utils/ui_integration.py +++ b/scion/utils/ui_integration.py @@ -8,8 +8,8 @@ import asyncio from typing import Optional -from openspace.utils.logging import Logger -from openspace.utils.ui import AgentStatus, OpenSpaceUI +from scion.utils.logging import Logger +from scion.utils.ui import AgentStatus, OpenSpaceUI logger = Logger.get_logger(__name__) diff --git a/tests/conftest.py b/tests/conftest.py index 02b26f8..53bcc0b 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -50,9 +50,9 @@ def in_memory_store(tmp_path: Path): class can open its own connection with the path it expects. The file is cleaned up automatically by pytest's ``tmp_path``. """ - from openspace.skill_engine.store import SkillStore + from scion.skill_engine.store import SkillStore - db_path = tmp_path / "test_openspace.db" + db_path = tmp_path / "test_scion.db" store = SkillStore(db_path=db_path) yield store store.close() diff --git a/tests/mocks/llm.py b/tests/mocks/llm.py index 044ae97..c39a2fe 100644 --- a/tests/mocks/llm.py +++ b/tests/mocks/llm.py @@ -1,7 +1,7 @@ """Deterministic LLM mock for testing. Provides ``MockLLMClient`` — a drop-in replacement for -``openspace.llm.LLMClient`` that returns pre-configured responses +``scion.llm.LLMClient`` that returns pre-configured responses from a response pool instead of calling a real LLM API. Usage in tests:: diff --git a/tests/test_analysis_store.py b/tests/test_analysis_store.py index 8d79a4b..641565f 100644 --- a/tests/test_analysis_store.py +++ b/tests/test_analysis_store.py @@ -7,8 +7,8 @@ from pathlib import Path from typing import List -from openspace.skill_engine.analysis_store import AnalysisStore -from openspace.skill_engine.types import ( +from scion.skill_engine.analysis_store import AnalysisStore +from scion.skill_engine.types import ( ExecutionAnalysis, EvolutionSuggestion, EvolutionType, @@ -343,7 +343,7 @@ def test_shared_connection_mode(self, temp_db_path): lock = threading.Lock() # Initialize the schema manually since we're not using standalone mode - from openspace.skill_engine.migration_manager import MigrationManager + from scion.skill_engine.migration_manager import MigrationManager migration_manager = MigrationManager(conn=conn, lock=lock) migration_manager.ensure_current_schema() @@ -375,7 +375,7 @@ def test_shared_connection_mode(self, temp_db_path): def test_analysis_store_integration_with_skill_store(temp_db_path): """Test AnalysisStore integration with SkillStore.""" - from openspace.skill_engine.store import SkillStore + from scion.skill_engine.store import SkillStore store = SkillStore(db_path=temp_db_path) @@ -474,7 +474,7 @@ def test_bulk_upsert_atomicity_with_validation_error(temp_db_path): lock = threading.Lock() # Initialize schema - from openspace.skill_engine.migration_manager import MigrationManager + from scion.skill_engine.migration_manager import MigrationManager migration_manager = MigrationManager(conn=conn, lock=lock) migration_manager.ensure_current_schema() @@ -525,7 +525,7 @@ def test_store_calls_public_insert_analysis_method(tmp_path): # This test verifies the layering fix - store.py should call public methods temp_db_path = tmp_path / "test_public_method.db" - from openspace.skill_engine.store import SkillStore + from scion.skill_engine.store import SkillStore store = SkillStore(db_path=temp_db_path) diff --git a/tests/test_appcontainer.py b/tests/test_appcontainer.py index dc9d26e..d48058a 100644 --- a/tests/test_appcontainer.py +++ b/tests/test_appcontainer.py @@ -21,15 +21,15 @@ import pytest -from openspace.app.container import AppContainer -from openspace.app.factory import ( +from scion.app.container import AppContainer +from scion.app.factory import ( _StubLLM, _StubSkillStore, _StubTelemetry, build_container, build_test_container, ) -from openspace.domain.ports import LLMClientPort, SkillStorePort, TelemetryPort +from scion.domain.ports import LLMClientPort, SkillStorePort, TelemetryPort # ══════════════════════════════════════════════════════════════════════ # Container construction diff --git a/tests/test_architecture_boundaries.py b/tests/test_architecture_boundaries.py index a4e32c0..9c2b518 100644 --- a/tests/test_architecture_boundaries.py +++ b/tests/test_architecture_boundaries.py @@ -26,7 +26,7 @@ # --------------------------------------------------------------------------- _REPO_ROOT = Path(__file__).resolve().parent.parent -_OPENSPACE = _REPO_ROOT / "openspace" +_OPENSPACE = _REPO_ROOT / "scion" _DOMAIN = _OPENSPACE / "domain" # stdlib top-level modules that are always allowed everywhere @@ -91,7 +91,7 @@ ) # All allowed import roots for domain layer -_DOMAIN_ALLOWED_ROOTS: frozenset[str] = _STDLIB_PREFIXES | _DOMAIN_ALLOWED_THIRD_PARTY | frozenset({"openspace.domain"}) +_DOMAIN_ALLOWED_ROOTS: frozenset[str] = _STDLIB_PREFIXES | _DOMAIN_ALLOWED_THIRD_PARTY | frozenset({"scion.domain"}) def _collect_py_files(directory: Path) -> list[Path]: @@ -134,10 +134,10 @@ def _is_allowed_domain_import(module: str) -> bool: # stdlib or allowed third-party if root in _DOMAIN_ALLOWED_ROOTS: return True - # intra-domain (openspace.domain.*) - if module.startswith("openspace.domain"): + # intra-domain (scion.domain.*) + if module.startswith("scion.domain"): return True - # relative imports within domain resolve to openspace.domain + # relative imports within domain resolve to scion.domain # (already handled by ast — from .ports import X becomes module="ports") # Single-segment names matching stdlib if root in _STDLIB_PREFIXES: @@ -153,8 +153,8 @@ def _is_allowed_domain_import(module: str) -> bool: class TestDomainImportPurity: """Domain layer (openspace/domain/) must import ZERO infrastructure modules. - Allowed: stdlib, structlog, openspace.domain.* - Forbidden: anything else in openspace.* or third-party infra packages. + Allowed: stdlib, structlog, scion.domain.* + Forbidden: anything else in scion.* or third-party infra packages. """ def test_known_cross_layer_count(self) -> None: @@ -177,9 +177,9 @@ def test_domain_files_exist(self) -> None: # The test ensures NO NEW violations are introduced. _KNOWN_CROSS_LAYER: frozenset[tuple[str, str]] = frozenset( { - ("openspace/domain/enums.py", "openspace.skill_engine.types"), - ("openspace/domain/enums.py", "openspace.grounding.core.types"), - ("openspace/domain/enums.py", "openspace.grounding.core.exceptions"), + ("scion/domain/enums.py", "scion.skill_engine.types"), + ("scion/domain/enums.py", "scion.grounding.core.types"), + ("scion/domain/enums.py", "scion.grounding.core.exceptions"), } ) @@ -200,14 +200,14 @@ def test_domain_imports_no_infrastructure(self) -> None: pytest.fail(f"Domain layer has {len(violations)} NEW forbidden import(s):\n{detail}") def test_domain_does_not_import_openspace_infra(self) -> None: - """Domain must not import from openspace.* outside openspace.domain.""" + """Domain must not import from scion.* outside scion.domain.""" violations: list[str] = [] for filepath in self._domain_files(): rel = filepath.relative_to(_REPO_ROOT) rel_posix = rel.as_posix() for module in _extract_imports(filepath): - if module.startswith("openspace.") and not module.startswith("openspace.domain"): + if module.startswith("scion.") and not module.startswith("scion.domain"): if (rel_posix, module) not in self._KNOWN_CROSS_LAYER: violations.append(f" {rel}: imports '{module}'") @@ -268,15 +268,15 @@ def test_mcp_handler_files_exist(self) -> None: # public property accessors as delegation is fully wired in Phase 4). _KNOWN_PRIVATE_ACCESS: frozenset[tuple[str, int, str]] = frozenset( { - ("openspace/mcp_server.py", 203, "_skill_store"), - ("openspace/mcp_server.py", 221, "_grounding_config"), - ("openspace/mcp_server.py", 314, "_skill_registry"), - ("openspace/mcp_server.py", 448, "_skill_registry"), - ("openspace/mcp_server.py", 752, "_skill_registry"), - ("openspace/mcp_server.py", 653, "_skill_registry"), - ("openspace/mcp_server.py", 771, "_skill_evolver"), - ("openspace/mcp_server.py", 755, "_skill_evolver"), - ("openspace/mcp_server.py", 437, "_grounding_config"), + ("scion/mcp_server.py", 203, "_skill_store"), + ("scion/mcp_server.py", 221, "_grounding_config"), + ("scion/mcp_server.py", 314, "_skill_registry"), + ("scion/mcp_server.py", 448, "_skill_registry"), + ("scion/mcp_server.py", 752, "_skill_registry"), + ("scion/mcp_server.py", 653, "_skill_registry"), + ("scion/mcp_server.py", 771, "_skill_evolver"), + ("scion/mcp_server.py", 755, "_skill_evolver"), + ("scion/mcp_server.py", 437, "_grounding_config"), } ) diff --git a/tests/test_ast_scanner.py b/tests/test_ast_scanner.py index 11aec24..39473e7 100644 --- a/tests/test_ast_scanner.py +++ b/tests/test_ast_scanner.py @@ -11,8 +11,8 @@ import pytest -from openspace.security import check_code_safety -from openspace.security.ast_scanner import ( +from scion.security import check_code_safety +from scion.security.ast_scanner import ( Finding, Severity, load_blocklist, diff --git a/tests/test_auth_integration.py b/tests/test_auth_integration.py index 6610f11..bfd19ff 100644 --- a/tests/test_auth_integration.py +++ b/tests/test_auth_integration.py @@ -21,8 +21,8 @@ import pytest -from openspace.auth.bearer import BearerTokenMiddleware -from openspace.auth.rate_limit import ( +from scion.auth.bearer import BearerTokenMiddleware +from scion.auth.rate_limit import ( RATE_LIMIT_PER_IP_ENV, RATE_LIMIT_PER_TOKEN_ENV, RATE_LIMIT_WINDOW_ENV, diff --git a/tests/test_auth_provider.py b/tests/test_auth_provider.py index 584231b..81759dc 100644 --- a/tests/test_auth_provider.py +++ b/tests/test_auth_provider.py @@ -1,4 +1,4 @@ -"""Tests for openspace.auth.provider — EPIC 2.5. +"""Tests for scion.auth.provider — EPIC 2.5. Covers: - #104: Token creation, validation, claims model @@ -17,7 +17,7 @@ import pytest -from openspace.auth.provider import ( +from scion.auth.provider import ( _MAX_TTL_SECONDS, DEFAULT_TOOL_POLICIES, TIER_DEFAULT_SCOPES, @@ -40,7 +40,7 @@ create_token, validate_token, ) -from openspace.sandbox.leases import TrustTier +from scion.sandbox.leases import TrustTier # Shared test secret (>= 32 chars) TEST_SECRET = "test-secret-key-for-hmac-signing-at-least-32-chars" diff --git a/tests/test_auto_import_disabled.py b/tests/test_auto_import_disabled.py index 930625d..ee0cb18 100644 --- a/tests/test_auto_import_disabled.py +++ b/tests/test_auto_import_disabled.py @@ -20,25 +20,25 @@ class TestAutoImportConfigDefaults: """SkillConfig.auto_import_enabled must default to False.""" def test_default_is_false(self): - from openspace.config.grounding import SkillConfig + from scion.config.grounding import SkillConfig cfg = SkillConfig() assert cfg.auto_import_enabled is False def test_explicit_true(self): - from openspace.config.grounding import SkillConfig + from scion.config.grounding import SkillConfig cfg = SkillConfig(auto_import_enabled=True) assert cfg.auto_import_enabled is True def test_explicit_false(self): - from openspace.config.grounding import SkillConfig + from scion.config.grounding import SkillConfig cfg = SkillConfig(auto_import_enabled=False) assert cfg.auto_import_enabled is False def test_serialization_roundtrip(self): - from openspace.config.grounding import SkillConfig + from scion.config.grounding import SkillConfig cfg = SkillConfig(auto_import_enabled=True) data = cfg.model_dump() @@ -56,7 +56,7 @@ class TestIsAutoImportEnabled: """_is_auto_import_enabled() must reflect SkillConfig state.""" def test_returns_false_when_no_instance(self): - import openspace.mcp_server as srv + import scion.mcp_server as srv original = srv._openspace_instance try: @@ -66,7 +66,7 @@ def test_returns_false_when_no_instance(self): srv._openspace_instance = original def test_returns_false_when_not_initialized(self): - import openspace.mcp_server as srv + import scion.mcp_server as srv original = srv._openspace_instance try: @@ -78,7 +78,7 @@ def test_returns_false_when_not_initialized(self): srv._openspace_instance = original def test_returns_false_when_config_missing(self): - import openspace.mcp_server as srv + import scion.mcp_server as srv original = srv._openspace_instance try: @@ -91,7 +91,7 @@ def test_returns_false_when_config_missing(self): srv._openspace_instance = original def test_returns_false_when_skills_config_missing(self): - import openspace.mcp_server as srv + import scion.mcp_server as srv original = srv._openspace_instance try: @@ -106,8 +106,8 @@ def test_returns_false_when_skills_config_missing(self): srv._openspace_instance = original def test_returns_false_when_flag_is_false(self): - import openspace.mcp_server as srv - from openspace.config.grounding import SkillConfig + import scion.mcp_server as srv + from scion.config.grounding import SkillConfig original = srv._openspace_instance try: @@ -122,8 +122,8 @@ def test_returns_false_when_flag_is_false(self): srv._openspace_instance = original def test_returns_true_when_flag_is_true(self): - import openspace.mcp_server as srv - from openspace.config.grounding import SkillConfig + import scion.mcp_server as srv + from scion.config.grounding import SkillConfig original = srv._openspace_instance try: @@ -148,19 +148,19 @@ class TestCloudSearchAndImportGating: @pytest.fixture(autouse=True) def _patch_auto_import(self): - with patch("openspace.mcp_server._is_auto_import_enabled", return_value=False): + with patch("scion.mcp_server._is_auto_import_enabled", return_value=False): yield async def test_returns_empty_when_disabled(self): - from openspace.mcp_server import _cloud_search_and_import + from scion.mcp_server import _cloud_search_and_import result = await _cloud_search_and_import("build a web scraper") assert result == [] async def test_never_calls_cloud_when_disabled(self): """Cloud search module should never be imported when disabled.""" - with patch("openspace.mcp_server._is_auto_import_enabled", return_value=False): - from openspace.mcp_server import _cloud_search_and_import + with patch("scion.mcp_server._is_auto_import_enabled", return_value=False): + from scion.mcp_server import _cloud_search_and_import # If cloud modules were imported, this would fail on missing deps result = await _cloud_search_and_import("anything") @@ -173,8 +173,8 @@ class TestCloudSearchAndImportEnabled: async def test_proceeds_when_enabled(self): """Verify the guard allows through when enabled (will fail on missing cloud module, proving the guard was passed).""" - with patch("openspace.mcp_server._is_auto_import_enabled", return_value=True): - from openspace.mcp_server import _cloud_search_and_import + with patch("scion.mcp_server._is_auto_import_enabled", return_value=True): + from scion.mcp_server import _cloud_search_and_import # Cloud modules won't be available in test env, so this should # return [] via the except branch, but it should NOT return @@ -193,8 +193,8 @@ class TestDoImportCloudSkillGating: """_do_import_cloud_skill must refuse when auto-import is disabled.""" async def test_blocked_when_disabled(self): - with patch("openspace.mcp_server._is_auto_import_enabled", return_value=False): - from openspace.mcp_server import _do_import_cloud_skill + with patch("scion.mcp_server._is_auto_import_enabled", return_value=False): + from scion.mcp_server import _do_import_cloud_skill result = await _do_import_cloud_skill("some-skill-id") assert result["status"] == "blocked" @@ -203,8 +203,8 @@ async def test_blocked_when_disabled(self): async def test_allowed_when_enabled(self): """When enabled, should attempt to actually import (and fail on missing cloud client — proving the guard was passed).""" - with patch("openspace.mcp_server._is_auto_import_enabled", return_value=True): - from openspace.mcp_server import _do_import_cloud_skill + with patch("scion.mcp_server._is_auto_import_enabled", return_value=True): + from scion.mcp_server import _do_import_cloud_skill with pytest.raises(Exception): # Will fail because cloud client isn't configured @@ -237,11 +237,11 @@ def _patch_openspace(self): ) with ( - patch("openspace.mcp_server._get_openspace", new_callable=AsyncMock, return_value=mock_os), - patch("openspace.mcp_server._get_store") as mock_store, - patch("openspace.cloud.search.hybrid_search_skills", mock_hybrid, create=True), - patch("openspace.mcp_server._is_auto_import_enabled", return_value=False), - patch("openspace.mcp_server._do_import_cloud_skill", new_callable=AsyncMock) as mock_import, + patch("scion.mcp_server._get_openspace", new_callable=AsyncMock, return_value=mock_os), + patch("scion.mcp_server._get_store") as mock_store, + patch("scion.cloud.search.hybrid_search_skills", mock_hybrid, create=True), + patch("scion.mcp_server._is_auto_import_enabled", return_value=False), + patch("scion.mcp_server._do_import_cloud_skill", new_callable=AsyncMock) as mock_import, ): mock_store.return_value = MagicMock() self.mock_import = mock_import @@ -250,7 +250,7 @@ def _patch_openspace(self): async def test_auto_import_param_true_but_config_false(self): """Even with auto_import=True in the call, config flag blocks import.""" - from openspace.mcp_server import search_skills + from scion.mcp_server import search_skills result_json = await search_skills(query="web scraper", auto_import=True) result = json.loads(result_json) @@ -261,7 +261,7 @@ async def test_auto_import_param_true_but_config_false(self): assert len(result["results"]) == 1 async def test_no_import_summary_when_disabled(self): - from openspace.mcp_server import search_skills + from scion.mcp_server import search_skills result_json = await search_skills(query="web scraper", auto_import=True) result = json.loads(result_json) @@ -281,13 +281,13 @@ async def test_cloud_import_blocked_in_execute_task(self): """Cloud import in execute_task goes through _cloud_search_and_import, which is gated. Verify the chain works.""" with ( - patch("openspace.mcp_server._is_auto_import_enabled", return_value=False), - patch("openspace.mcp_server._cloud_search_and_import", new_callable=AsyncMock) as mock_cloud, + patch("scion.mcp_server._is_auto_import_enabled", return_value=False), + patch("scion.mcp_server._cloud_search_and_import", new_callable=AsyncMock) as mock_cloud, ): # Even though _cloud_search_and_import has its own guard, # verify that when called, it returns [] without side effects mock_cloud.return_value = [] - from openspace.mcp_server import _cloud_search_and_import + from scion.mcp_server import _cloud_search_and_import result = await _cloud_search_and_import("any task") assert result == [] @@ -303,8 +303,8 @@ class TestConfigToGatingIntegration: to _is_auto_import_enabled() and gates all import paths.""" def test_config_false_gates_helper(self): - import openspace.mcp_server as srv - from openspace.config.grounding import SkillConfig + import scion.mcp_server as srv + from scion.config.grounding import SkillConfig original = srv._openspace_instance try: @@ -320,8 +320,8 @@ def test_config_false_gates_helper(self): srv._openspace_instance = original def test_config_true_enables_helper(self): - import openspace.mcp_server as srv - from openspace.config.grounding import SkillConfig + import scion.mcp_server as srv + from scion.config.grounding import SkillConfig original = srv._openspace_instance try: diff --git a/tests/test_benchmark_extraction.py b/tests/test_benchmark_extraction.py index 2fc8c20..aa866ff 100644 --- a/tests/test_benchmark_extraction.py +++ b/tests/test_benchmark_extraction.py @@ -17,7 +17,7 @@ import pytest # Production package root -_OPENSPACE_ROOT = Path(__file__).resolve().parent.parent / "openspace" +_OPENSPACE_ROOT = Path(__file__).resolve().parent.parent / "scion" # Files that are explicitly allowed to reference gdpval_bench # (dashboard is not part of the MCP server) @@ -79,11 +79,11 @@ def test_allowed_files_list_is_minimal(self): @pytest.mark.parametrize( "module_path", [ - "openspace.llm.client", - "openspace.skill_engine.registry", - "openspace.skill_engine.evolver", - "openspace.skill_engine.analyzer", - "openspace.grounding.core.quality.manager", + "scion.llm.client", + "scion.skill_engine.registry", + "scion.skill_engine.evolver", + "scion.skill_engine.analyzer", + "scion.grounding.core.quality.manager", ], ) def test_previously_coupled_modules_clean(self, module_path): @@ -98,7 +98,7 @@ def test_previously_coupled_modules_clean(self, module_path): class TestBenchmarkIsolation: - """gdpval_bench must be completely separate from openspace runtime.""" + """gdpval_bench must be completely separate from scion runtime.""" def test_gdpval_bench_not_in_sys_modules_after_openspace_import(self): """Importing openspace modules must not pull in gdpval_bench.""" @@ -109,9 +109,9 @@ def test_gdpval_bench_not_in_sys_modules_after_openspace_import(self): try: # Force reimport of the previously-coupled modules for mod_name in [ - "openspace.skill_engine.registry", - "openspace.skill_engine.evolver", - "openspace.skill_engine.analyzer", + "scion.skill_engine.registry", + "scion.skill_engine.evolver", + "scion.skill_engine.analyzer", ]: try: if mod_name in sys.modules: diff --git a/tests/test_capability_leases.py b/tests/test_capability_leases.py index cdc26a2..e4ea069 100644 --- a/tests/test_capability_leases.py +++ b/tests/test_capability_leases.py @@ -23,9 +23,9 @@ import pytest from pydantic import ValidationError -from openspace.domain.ports import CapabilityLeaseResolverPort -from openspace.domain.types import CapabilityLease, SandboxPolicy -from openspace.sandbox.leases import ( +from scion.domain.ports import CapabilityLeaseResolverPort +from scion.domain.types import CapabilityLease, SandboxPolicy +from scion.sandbox.leases import ( REQUIRED_BLOCKED_COMMANDS, REQUIRED_BLOCKED_DOMAINS, REQUIRED_DENIED_PATHS, diff --git a/tests/test_delegation.py b/tests/test_delegation.py index 1202012..cce214b 100644 --- a/tests/test_delegation.py +++ b/tests/test_delegation.py @@ -19,12 +19,12 @@ import pytest -from openspace.app.container import AppContainer -from openspace.app.factory import _StubLLM, _StubTelemetry +from scion.app.container import AppContainer +from scion.app.factory import _StubLLM, _StubTelemetry # tool_layer imports litellm which may not be available in all test envs try: - from openspace.tool_layer import OpenSpace, OpenSpaceConfig + from scion.tool_layer import OpenSpace, OpenSpaceConfig _HAS_TOOL_LAYER = True except (ImportError, ModuleNotFoundError): @@ -32,7 +32,7 @@ pytestmark = pytest.mark.skipif( not _HAS_TOOL_LAYER, - reason="openspace.tool_layer requires litellm (not installed or broken)", + reason="scion.tool_layer requires litellm (not installed or broken)", ) diff --git a/tests/test_domain_exceptions.py b/tests/test_domain_exceptions.py index 9f722b1..46ed076 100644 --- a/tests/test_domain_exceptions.py +++ b/tests/test_domain_exceptions.py @@ -25,7 +25,7 @@ class TestExceptionHierarchy: """All domain exceptions inherit from OpenSpaceError.""" def test_all_exceptions_importable(self): - from openspace.domain.exceptions import ( + from scion.domain.exceptions import ( ConfigurationError, DependencyError, EvolutionError, @@ -57,7 +57,7 @@ def test_all_exceptions_importable(self): assert len(all_exc) == 12 def test_all_subclass_openspace_error(self): - from openspace.domain.exceptions import ( + from scion.domain.exceptions import ( ConfigurationError, DependencyError, EvolutionError, @@ -89,12 +89,12 @@ def test_all_subclass_openspace_error(self): assert issubclass(exc_cls, Exception) def test_openspace_error_is_exception(self): - from openspace.domain.exceptions import OpenSpaceError + from scion.domain.exceptions import OpenSpaceError assert issubclass(OpenSpaceError, Exception) def test_can_catch_all_with_openspace_error(self): - from openspace.domain.exceptions import ( + from scion.domain.exceptions import ( ExecutionError, NotFoundError, OpenSpaceError, @@ -136,7 +136,7 @@ class TestErrorCodes: ], ) def test_error_code_mapping(self, exc_cls: str, expected_code: str): - import openspace.domain.exceptions as mod + import scion.domain.exceptions as mod cls = getattr(mod, exc_cls) exc = cls("test message") @@ -152,7 +152,7 @@ class TestSerialization: """to_dict() and __str__/__repr__ work correctly.""" def test_to_dict_basic(self): - from openspace.domain.exceptions import ValidationError + from scion.domain.exceptions import ValidationError exc = ValidationError("bad input", field="name") d = exc.to_dict() @@ -162,7 +162,7 @@ def test_to_dict_basic(self): assert d["context"]["field"] == "name" def test_to_dict_with_retryable(self): - from openspace.domain.exceptions import ExternalServiceError + from scion.domain.exceptions import ExternalServiceError exc = ExternalServiceError("API down", service="cloud", status_code=503) d = exc.to_dict() @@ -170,7 +170,7 @@ def test_to_dict_with_retryable(self): assert d["context"]["service"] == "cloud" def test_str_includes_error_code(self): - from openspace.domain.exceptions import NotFoundError + from scion.domain.exceptions import NotFoundError exc = NotFoundError("skill", skill_id="abc-123") s = str(exc) @@ -178,7 +178,7 @@ def test_str_includes_error_code(self): assert "skill not found: abc-123" in s def test_repr_includes_class_name(self): - from openspace.domain.exceptions import ExecutionError + from scion.domain.exceptions import ExecutionError exc = ExecutionError("task failed", task_id="t42") r = repr(exc) @@ -195,7 +195,7 @@ class TestContextPropagation: """Context kwargs are preserved in .context dict.""" def test_context_stored(self): - from openspace.domain.exceptions import ExecutionError + from scion.domain.exceptions import ExecutionError exc = ExecutionError("boom", task_id="t1", tool_name="bash", iteration=3) assert exc.context["task_id"] == "t1" @@ -203,33 +203,33 @@ def test_context_stored(self): assert exc.context["iteration"] == 3 def test_empty_context(self): - from openspace.domain.exceptions import ValidationError + from scion.domain.exceptions import ValidationError exc = ValidationError("no context") assert exc.context == {} def test_not_found_resource_type(self): - from openspace.domain.exceptions import NotFoundError + from scion.domain.exceptions import NotFoundError exc = NotFoundError("session", session_name="default") assert exc.resource_type == "session" assert "session not found: default" in exc.message def test_not_found_without_id(self): - from openspace.domain.exceptions import NotFoundError + from scion.domain.exceptions import NotFoundError exc = NotFoundError("skill") assert "skill not found" in exc.message assert ":" not in exc.message # No ID appended def test_dependency_error_stores_dependency(self): - from openspace.domain.exceptions import DependencyError + from scion.domain.exceptions import DependencyError exc = DependencyError("npm not found", dependency="npm") assert exc.dependency == "npm" def test_external_service_error_stores_service(self): - from openspace.domain.exceptions import ExternalServiceError + from scion.domain.exceptions import ExternalServiceError exc = ExternalServiceError("timeout", service="cloud-api", status_code=504) assert exc.service == "cloud-api" @@ -245,28 +245,28 @@ class TestRetryable: """Retryable defaults are correct and overridable.""" def test_default_not_retryable(self): - from openspace.domain.exceptions import ValidationError + from scion.domain.exceptions import ValidationError assert ValidationError("x").retryable is False def test_timeout_default_retryable(self): - from openspace.domain.exceptions import OperationTimeoutError + from scion.domain.exceptions import OperationTimeoutError assert OperationTimeoutError("x").retryable is True def test_external_service_default_retryable(self): - from openspace.domain.exceptions import ExternalServiceError + from scion.domain.exceptions import ExternalServiceError # No status_code → default retryable assert ExternalServiceError("x").retryable is True def test_external_service_4xx_not_retryable(self): - from openspace.domain.exceptions import ExternalServiceError + from scion.domain.exceptions import ExternalServiceError assert ExternalServiceError("x", status_code=401).retryable is False def test_external_service_any_4xx_not_retryable(self): - from openspace.domain.exceptions import ExternalServiceError + from scion.domain.exceptions import ExternalServiceError # All 4xx (except 429) should be non-retryable for code in [ @@ -301,21 +301,21 @@ def test_external_service_any_4xx_not_retryable(self): assert exc.retryable is False, f"status_code={code} should NOT be retryable" def test_external_service_429_is_retryable(self): - from openspace.domain.exceptions import ExternalServiceError + from scion.domain.exceptions import ExternalServiceError assert ExternalServiceError("x", status_code=429).retryable is True assert ExternalServiceError("x", status_code=403).retryable is False assert ExternalServiceError("x", status_code=404).retryable is False def test_external_service_5xx_retryable(self): - from openspace.domain.exceptions import ExternalServiceError + from scion.domain.exceptions import ExternalServiceError assert ExternalServiceError("x", status_code=500).retryable is True assert ExternalServiceError("x", status_code=503).retryable is True assert ExternalServiceError("x", status_code=429).retryable is True def test_external_service_edge_case_status_codes(self): - from openspace.domain.exceptions import ExternalServiceError + from scion.domain.exceptions import ExternalServiceError # Boundary: 399 is outside 4xx range → optimistic retry assert ExternalServiceError("x", status_code=399).retryable is True @@ -329,19 +329,19 @@ def test_external_service_edge_case_status_codes(self): assert ExternalServiceError("x", status_code=200).retryable is True def test_override_retryable(self): - from openspace.domain.exceptions import ValidationError + from scion.domain.exceptions import ValidationError exc = ValidationError("retry me", retryable=True) assert exc.retryable is True def test_override_non_retryable(self): - from openspace.domain.exceptions import OperationTimeoutError + from scion.domain.exceptions import OperationTimeoutError exc = OperationTimeoutError("no retry", retryable=False) assert exc.retryable is False def test_external_service_override_retryable(self): - from openspace.domain.exceptions import ExternalServiceError + from scion.domain.exceptions import ExternalServiceError # Override: force retryable even on 401 exc = ExternalServiceError("x", status_code=401, retryable=True) @@ -357,14 +357,14 @@ class TestClientMessage: """safe_message / client_message handling.""" def test_client_message_defaults_to_generic(self): - from openspace.domain.exceptions import ExecutionError + from scion.domain.exceptions import ExecutionError exc = ExecutionError("internal details here") # Without safe_message, client_message returns generic (never raw) assert exc.client_message == "An internal error occurred" def test_client_message_uses_safe_message(self): - from openspace.domain.exceptions import ExecutionError + from scion.domain.exceptions import ExecutionError exc = ExecutionError( "NullPointerException at line 42", @@ -374,7 +374,7 @@ def test_client_message_uses_safe_message(self): assert exc.message == "NullPointerException at line 42" def test_to_safe_dict_redacts(self): - from openspace.domain.exceptions import ExecutionError + from scion.domain.exceptions import ExecutionError exc = ExecutionError( "secret path /opt/secrets/key.pem", @@ -387,7 +387,7 @@ def test_to_safe_dict_redacts(self): assert safe["error_code"] == "EXECUTION_ERROR" def test_to_safe_dict_without_safe_message(self): - from openspace.domain.exceptions import ExecutionError + from scion.domain.exceptions import ExecutionError exc = ExecutionError("internal details") safe = exc.to_safe_dict() @@ -403,7 +403,7 @@ class TestMapToMCPErrorCode: """map_to_mcp_error_code() handles all exception types.""" def test_domain_exceptions_map_correctly(self): - from openspace.domain.exceptions import ( + from scion.domain.exceptions import ( ConfigurationError, DependencyError, ExecutionError, @@ -429,33 +429,33 @@ def test_domain_exceptions_map_correctly(self): assert map_to_mcp_error_code(InternalError("x")) == "INTERNAL_ERROR" def test_unknown_exception_maps_to_internal(self): - from openspace.domain.exceptions import map_to_mcp_error_code + from scion.domain.exceptions import map_to_mcp_error_code assert map_to_mcp_error_code(RuntimeError("oops")) == "INTERNAL_ERROR" assert map_to_mcp_error_code(Exception("generic")) == "INTERNAL_ERROR" def test_builtin_timeout_maps_to_timeout(self): - from openspace.domain.exceptions import map_to_mcp_error_code + from scion.domain.exceptions import map_to_mcp_error_code assert map_to_mcp_error_code(TimeoutError("t")) == "TIMEOUT_ERROR" def test_builtin_permission_maps_to_denied(self): - from openspace.domain.exceptions import map_to_mcp_error_code + from scion.domain.exceptions import map_to_mcp_error_code assert map_to_mcp_error_code(PermissionError("p")) == "PERMISSION_DENIED" def test_builtin_file_not_found_maps_to_not_found(self): - from openspace.domain.exceptions import map_to_mcp_error_code + from scion.domain.exceptions import map_to_mcp_error_code assert map_to_mcp_error_code(FileNotFoundError("f")) == "SKILL_NOT_FOUND" def test_builtin_value_error_maps_to_validation(self): - from openspace.domain.exceptions import map_to_mcp_error_code + from scion.domain.exceptions import map_to_mcp_error_code assert map_to_mcp_error_code(ValueError("bad")) == "VALIDATION_ERROR" def test_base_openspace_error_maps_to_internal(self): - from openspace.domain.exceptions import OpenSpaceError, map_to_mcp_error_code + from scion.domain.exceptions import OpenSpaceError, map_to_mcp_error_code assert map_to_mcp_error_code(OpenSpaceError("x")) == "INTERNAL_ERROR" @@ -469,8 +469,8 @@ class TestIntegrationWithExistingErrors: """Domain exceptions work with existing error helpers.""" def test_sanitize_error_handles_domain_exception(self): - from openspace.domain.exceptions import ExecutionError - from openspace.errors import sanitize_error + from scion.domain.exceptions import ExecutionError + from scion.errors import sanitize_error exc = ExecutionError("simple error message") safe = sanitize_error(exc) @@ -479,8 +479,8 @@ def test_sanitize_error_handles_domain_exception(self): def test_handle_mcp_exception_with_domain_exception(self): import json - from openspace.domain.exceptions import ValidationError - from openspace.errors import handle_mcp_exception + from scion.domain.exceptions import ValidationError + from scion.errors import handle_mcp_exception result = handle_mcp_exception( ValidationError("bad input", safe_message="Invalid request"), @@ -496,8 +496,8 @@ def test_handle_mcp_exception_with_domain_exception(self): def test_handle_mcp_exception_prefers_domain_error_code(self): import json - from openspace.domain.exceptions import NotFoundError - from openspace.errors import handle_mcp_exception + from scion.domain.exceptions import NotFoundError + from scion.errors import handle_mcp_exception # Even though we pass error_code=EXECUTION_ERROR, the domain # exception's error_code should win @@ -512,8 +512,8 @@ def test_handle_mcp_exception_prefers_domain_error_code(self): def test_handle_mcp_exception_generic_fallback_without_safe_message(self): import json - from openspace.domain.exceptions import ExecutionError - from openspace.errors import handle_mcp_exception + from scion.domain.exceptions import ExecutionError + from scion.errors import handle_mcp_exception # Without safe_message, client_message returns generic fallback result = handle_mcp_exception( @@ -529,7 +529,7 @@ def test_handle_mcp_exception_generic_fallback_without_safe_message(self): def test_handle_mcp_exception_maps_builtin_timeout(self): import json - from openspace.errors import handle_mcp_exception + from scion.errors import handle_mcp_exception result = handle_mcp_exception( TimeoutError("connection timed out"), @@ -542,7 +542,7 @@ def test_handle_mcp_exception_maps_builtin_timeout(self): def test_handle_mcp_exception_maps_builtin_permission(self): import json - from openspace.errors import handle_mcp_exception + from scion.errors import handle_mcp_exception result = handle_mcp_exception( PermissionError("access denied"), @@ -555,7 +555,7 @@ def test_handle_mcp_exception_maps_builtin_permission(self): def test_handle_mcp_exception_maps_builtin_value_error(self): import json - from openspace.errors import handle_mcp_exception + from scion.errors import handle_mcp_exception result = handle_mcp_exception( ValueError("invalid argument"), diff --git a/tests/test_domain_types_protocols.py b/tests/test_domain_types_protocols.py index 37823aa..66a69d7 100644 --- a/tests/test_domain_types_protocols.py +++ b/tests/test_domain_types_protocols.py @@ -25,7 +25,7 @@ class TestProtocolImports: """All 13 protocols must be importable and runtime-checkable.""" def test_all_protocols_importable(self): - from openspace.domain.ports import ( + from scion.domain.ports import ( AgentExecutorPort, AnalysisPort, AuthPort, @@ -59,7 +59,7 @@ def test_all_protocols_importable(self): assert len(protocols) == 13 def test_protocols_are_runtime_checkable(self): - from openspace.domain.ports import ( + from scion.domain.ports import ( AgentExecutorPort, AnalysisPort, AuthPort, @@ -107,7 +107,7 @@ class TestProtocolCompliance: def test_skill_store_has_required_methods(self): """SkillStore has the methods that SkillStorePort requires.""" - from openspace.skill_engine.store import SkillStore + from scion.skill_engine.store import SkillStore required_methods = [ "save_record", @@ -122,7 +122,7 @@ def test_skill_store_has_required_methods(self): def test_sandbox_has_required_methods(self): """BaseSandbox has the methods that SandboxPort requires.""" - from openspace.grounding.core.security.sandbox import BaseSandbox + from scion.grounding.core.security.sandbox import BaseSandbox required_methods = ["start", "stop", "execute_safe"] for method in required_methods: @@ -131,7 +131,7 @@ def test_sandbox_has_required_methods(self): def test_telemetry_has_required_methods(self): """Telemetry has capture/flush/shutdown (adapter bridges signature).""" try: - from openspace.utils.telemetry.telemetry import Telemetry + from scion.utils.telemetry.telemetry import Telemetry except ImportError: pytest.skip("Telemetry module not available") @@ -142,7 +142,7 @@ def test_telemetry_has_required_methods(self): def test_llm_client_has_complete(self): """LLMClient has the complete method.""" try: - from openspace.llm.client import LLMClient + from scion.llm.client import LLMClient except ImportError: pytest.skip("LLMClient not importable (litellm version issue)") @@ -150,7 +150,7 @@ def test_llm_client_has_complete(self): def test_policy_engine_has_required_methods(self): """SecurityPolicyManager has the methods PolicyEnginePort requires.""" - from openspace.grounding.core.security.policies import SecurityPolicyManager + from scion.grounding.core.security.policies import SecurityPolicyManager required_methods = [ "check_command_allowed", @@ -170,14 +170,14 @@ class TestTaskTypes: """TaskRequest and TaskResult are frozen and serializable.""" def test_task_request_frozen(self): - from openspace.domain.types import TaskRequest + from scion.domain.types import TaskRequest req = TaskRequest(task="test task", task_id="t1") with pytest.raises(FrozenInstanceError): req.task = "mutated" # type: ignore[misc] def test_task_request_from_dict(self): - from openspace.domain.types import TaskRequest + from scion.domain.types import TaskRequest data = { "task": "do something", @@ -196,7 +196,7 @@ def test_task_request_from_dict(self): assert req.context_dict == {"key": "value"} def test_task_request_deep_freezes_nested_context(self): - from openspace.domain.types import TaskRequest + from scion.domain.types import TaskRequest data = { "task": "test", @@ -213,14 +213,14 @@ def test_task_request_deep_freezes_nested_context(self): assert isinstance(val, tuple), f"Value for {key} not frozen: {type(val)}" def test_task_result_frozen(self): - from openspace.domain.types import TaskResult + from scion.domain.types import TaskResult result = TaskResult(task_id="t1", status="success", response="done") with pytest.raises(FrozenInstanceError): result.status = "error" # type: ignore[misc] def test_task_result_ok_property(self): - from openspace.domain.types import TaskResult + from scion.domain.types import TaskResult ok = TaskResult(task_id="t1", status="success") fail = TaskResult(task_id="t2", status="error") @@ -228,7 +228,7 @@ def test_task_result_ok_property(self): assert fail.ok is False def test_task_result_roundtrip(self): - from openspace.domain.types import TaskResult, ToolExecution + from scion.domain.types import TaskResult, ToolExecution original = TaskResult( task_id="t1", @@ -267,7 +267,7 @@ def test_task_result_roundtrip(self): assert restored.tool_executions[1].error == "not found" def test_task_result_replace(self): - from openspace.domain.types import TaskResult + from scion.domain.types import TaskResult original = TaskResult(task_id="t1", status="error", error="boom") fixed = replace(original, status="success", error=None) @@ -280,7 +280,7 @@ class TestSkillTypes: """SkillIdentity and SkillManifest are frozen.""" def test_skill_identity_hashable(self): - from openspace.domain.types import SkillIdentity + from scion.domain.types import SkillIdentity s1 = SkillIdentity(skill_id="s1", name="Skill One") s2 = SkillIdentity(skill_id="s1", name="Skill One Modified") @@ -291,14 +291,14 @@ def test_skill_identity_hashable(self): assert len(skills) == 2 # Different objects (frozen, full eq) def test_skill_identity_frozen(self): - from openspace.domain.types import SkillIdentity + from scion.domain.types import SkillIdentity s = SkillIdentity(skill_id="s1", name="test") with pytest.raises(FrozenInstanceError): s.name = "mutated" # type: ignore[misc] def test_skill_manifest_frozen(self): - from openspace.domain.types import SkillManifest + from scion.domain.types import SkillManifest m = SkillManifest( skill_id="s1", @@ -310,7 +310,7 @@ def test_skill_manifest_frozen(self): m.is_active = False # type: ignore[misc] def test_skill_manifest_effective_rate(self): - from openspace.domain.types import SkillManifest + from scion.domain.types import SkillManifest zero = SkillManifest(skill_id="s1", name="t", description="d", total_selections=0) assert zero.effective_rate == 0.0 @@ -325,7 +325,7 @@ def test_skill_manifest_effective_rate(self): assert active.effective_rate == pytest.approx(0.7) def test_skill_manifest_to_dict(self): - from openspace.domain.types import SkillManifest + from scion.domain.types import SkillManifest now = datetime.now() m = SkillManifest( @@ -345,14 +345,14 @@ class TestEvolutionTypes: """EvolutionRequest and EvolutionResult are frozen.""" def test_evolution_request_frozen(self): - from openspace.domain.types import EvolutionRequest + from scion.domain.types import EvolutionRequest req = EvolutionRequest(evolution_type="fix", trigger="analysis", target_skill_ids=("s1",)) with pytest.raises(FrozenInstanceError): req.direction = "mutated" # type: ignore[misc] def test_evolution_result_frozen(self): - from openspace.domain.types import EvolutionResult + from scion.domain.types import EvolutionResult res = EvolutionResult( success=True, @@ -367,7 +367,7 @@ class TestAnalysisTypes: """Analysis snapshots are frozen.""" def test_execution_analysis_snapshot_frozen(self): - from openspace.domain.types import ExecutionAnalysisSnapshot + from scion.domain.types import ExecutionAnalysisSnapshot snap = ExecutionAnalysisSnapshot( task_id="t1", @@ -379,7 +379,7 @@ def test_execution_analysis_snapshot_frozen(self): snap.task_completed = False # type: ignore[misc] def test_analysis_snapshot_nested_immutability(self): - from openspace.domain.types import ( + from scion.domain.types import ( ExecutionAnalysisSnapshot, SkillJudgmentSnapshot, ) @@ -398,14 +398,14 @@ class TestSearchTypes: """Search result types are frozen.""" def test_search_result_frozen(self): - from openspace.domain.types import SkillSearchResult + from scion.domain.types import SkillSearchResult r = SkillSearchResult(skill_id="s1", name="Test", description="desc", score=0.9) with pytest.raises(FrozenInstanceError): r.score = 0.1 # type: ignore[misc] def test_search_response_frozen(self): - from openspace.domain.types import SkillSearchResponse, SkillSearchResult + from scion.domain.types import SkillSearchResponse, SkillSearchResult resp = SkillSearchResponse( query="test", @@ -421,14 +421,14 @@ class TestSecurityTypes: """Sandbox policy and capability lease are frozen.""" def test_sandbox_policy_frozen(self): - from openspace.domain.types import SandboxPolicy + from scion.domain.types import SandboxPolicy p = SandboxPolicy(sandbox_enabled=True, trust_tier="basic") with pytest.raises(FrozenInstanceError): p.sandbox_enabled = False # type: ignore[misc] def test_capability_lease_frozen(self): - from openspace.domain.types import CapabilityLease + from scion.domain.types import CapabilityLease lease = CapabilityLease( lease_id="L1", @@ -444,14 +444,14 @@ class TestToolTypes: """Tool descriptor and call result are frozen.""" def test_tool_descriptor_frozen(self): - from openspace.domain.types import ToolDescriptor + from scion.domain.types import ToolDescriptor t = ToolDescriptor(name="bash", description="Run shell commands") with pytest.raises(FrozenInstanceError): t.name = "changed" # type: ignore[misc] def test_tool_call_result_frozen(self): - from openspace.domain.types import ToolCallResult + from scion.domain.types import ToolCallResult r = ToolCallResult(status="success", content="output") with pytest.raises(FrozenInstanceError): @@ -468,61 +468,61 @@ class TestEnumConsolidation: def test_re_exported_enums_match_originals(self): """Re-exported enums are the exact same objects.""" - from openspace.domain.enums import EvolutionType as DomainEvType - from openspace.skill_engine.types import EvolutionType as OrigEvType + from scion.domain.enums import EvolutionType as DomainEvType + from scion.skill_engine.types import EvolutionType as OrigEvType assert DomainEvType is OrigEvType - from openspace.domain.enums import BackendType as DomainBT - from openspace.grounding.core.types import BackendType as OrigBT + from scion.domain.enums import BackendType as DomainBT + from scion.grounding.core.types import BackendType as OrigBT assert DomainBT is OrigBT - from openspace.domain.enums import SkillCategory as DomainSC - from openspace.skill_engine.types import SkillCategory as OrigSC + from scion.domain.enums import SkillCategory as DomainSC + from scion.skill_engine.types import SkillCategory as OrigSC assert DomainSC is OrigSC def test_new_task_status_enum(self): - from openspace.domain.enums import TaskStatus + from scion.domain.enums import TaskStatus assert TaskStatus.SUCCESS.value == "success" assert TaskStatus.FAILED.value == "failed" assert TaskStatus.RUNNING.value == "running" def test_new_search_scope_enum(self): - from openspace.domain.enums import SearchScope + from scion.domain.enums import SearchScope assert SearchScope.ALL.value == "all" assert SearchScope.LOCAL.value == "local" assert SearchScope.CLOUD.value == "cloud" def test_new_trust_tier_enum(self): - from openspace.domain.enums import TrustTier + from scion.domain.enums import TrustTier assert TrustTier.UNTRUSTED.value == "untrusted" assert TrustTier.PRIVILEGED.value == "privileged" def test_new_skill_status_enum(self): - from openspace.domain.enums import SkillStatus + from scion.domain.enums import SkillStatus assert SkillStatus.ACTIVE.value == "active" assert SkillStatus.DEPRECATED.value == "deprecated" def test_new_mcp_error_code_enum(self): - from openspace.domain.enums import MCPErrorCode + from scion.domain.enums import MCPErrorCode assert MCPErrorCode.EXECUTION_ERROR.value == "EXECUTION_ERROR" assert MCPErrorCode.PERMISSION_DENIED.value == "PERMISSION_DENIED" def test_search_mode_enum(self): - from openspace.domain.enums import SearchMode + from scion.domain.enums import SearchMode assert SearchMode.HYBRID.value == "hybrid" assert SearchMode.SEMANTIC.value == "semantic" def test_patch_type_enum(self): - from openspace.domain.enums import PatchType + from scion.domain.enums import PatchType assert PatchType.AUTO.value == "auto" assert PatchType.DIFF.value == "diff" @@ -537,14 +537,14 @@ class TestDeepFreeze: """The _deep_freeze helper converts mutable containers recursively.""" def test_deep_freeze_dict(self): - from openspace.domain.types import _deep_freeze + from scion.domain.types import _deep_freeze result = _deep_freeze({"a": 1, "b": [2, 3]}) assert isinstance(result, tuple) assert result == (("a", 1), ("b", (2, 3))) def test_deep_freeze_nested(self): - from openspace.domain.types import _deep_freeze + from scion.domain.types import _deep_freeze result = _deep_freeze({"outer": {"inner": [1, {"deep": True}]}}) assert isinstance(result, tuple) @@ -554,13 +554,13 @@ def test_deep_freeze_nested(self): assert isinstance(val, tuple) def test_deep_freeze_set(self): - from openspace.domain.types import _deep_freeze + from scion.domain.types import _deep_freeze result = _deep_freeze({1, 2, 3}) assert isinstance(result, frozenset) def test_deep_freeze_scalar(self): - from openspace.domain.types import _deep_freeze + from scion.domain.types import _deep_freeze assert _deep_freeze(42) == 42 assert _deep_freeze("hello") == "hello" @@ -576,7 +576,7 @@ class TestAllTypesFrozen: """Every dataclass in domain.types MUST be frozen.""" def test_all_domain_types_are_frozen(self): - import openspace.domain.types as mod + import scion.domain.types as mod for name in mod.__all__: cls = getattr(mod, name) @@ -588,7 +588,7 @@ def test_all_domain_types_are_frozen(self): ) def test_all_domain_types_use_slots(self): - import openspace.domain.types as mod + import scion.domain.types as mod for name in mod.__all__: cls = getattr(mod, name) diff --git a/tests/test_e2b_sandbox_hardening.py b/tests/test_e2b_sandbox_hardening.py index 1e5d9e2..549bc36 100644 --- a/tests/test_e2b_sandbox_hardening.py +++ b/tests/test_e2b_sandbox_hardening.py @@ -29,20 +29,20 @@ class TestSandboxEnforcement: def test_mcp_config_defaults_sandbox_true(self): """MCPConfig.sandbox defaults to True.""" - from openspace.config.grounding import MCPConfig + from scion.config.grounding import MCPConfig cfg = MCPConfig() assert cfg.sandbox is True, "MCPConfig.sandbox must default to True" def test_config_grounding_json_sandbox_true(self): """config_grounding.json ships with sandbox: true.""" - config_path = ROOT / "openspace" / "config" / "config_grounding.json" + config_path = ROOT / "scion" / "config" / "config_grounding.json" data = json.loads(config_path.read_text()) assert data["mcp"]["sandbox"] is True, "config_grounding.json mcp.sandbox must be true" def test_config_security_json_sandbox_enabled(self): """config_security.json ships with sandbox_enabled: true globally.""" - config_path = ROOT / "openspace" / "config" / "config_security.json" + config_path = ROOT / "scion" / "config" / "config_security.json" data = json.loads(config_path.read_text()) policies = data["security_policies"] assert policies["global"]["sandbox_enabled"] is True, "Global sandbox_enabled must be true" @@ -53,7 +53,7 @@ def test_mcp_client_constructor_defaults_sandbox_true(self): """MCPClient.__init__ defaults sandbox=True.""" import inspect - from openspace.grounding.backends.mcp.client import MCPClient + from scion.grounding.backends.mcp.client import MCPClient sig = inspect.signature(MCPClient.__init__) default = sig.parameters["sandbox"].default @@ -63,7 +63,7 @@ def test_mcp_client_from_dict_defaults_sandbox_true(self): """MCPClient.from_dict defaults sandbox=True.""" import inspect - from openspace.grounding.backends.mcp.client import MCPClient + from scion.grounding.backends.mcp.client import MCPClient sig = inspect.signature(MCPClient.from_dict) default = sig.parameters["sandbox"].default @@ -73,7 +73,7 @@ def test_mcp_client_from_config_file_defaults_sandbox_true(self): """MCPClient.from_config_file defaults sandbox=True.""" import inspect - from openspace.grounding.backends.mcp.client import MCPClient + from scion.grounding.backends.mcp.client import MCPClient sig = inspect.signature(MCPClient.from_config_file) default = sig.parameters["sandbox"].default @@ -83,7 +83,7 @@ def test_create_connector_defaults_sandbox_true(self): """create_connector_from_config defaults sandbox=True.""" import inspect - from openspace.grounding.backends.mcp.config import create_connector_from_config + from scion.grounding.backends.mcp.config import create_connector_from_config sig = inspect.signature(create_connector_from_config) default = sig.parameters["sandbox"].default @@ -91,7 +91,7 @@ def test_create_connector_defaults_sandbox_true(self): def test_provider_extracts_sandbox_default_true(self): """MCPProvider reads sandbox with default True.""" - source = (ROOT / "openspace" / "grounding" / "backends" / "mcp" / "provider.py").read_text() + source = (ROOT / "scion" / "grounding" / "backends" / "mcp" / "provider.py").read_text() # Should have: get_config_value(config, "sandbox", True) assert 'get_config_value(config, "sandbox", True)' in source, ( "Provider must default sandbox to True via get_config_value" @@ -108,7 +108,7 @@ class TestConfigSourceRestriction: def test_e2b_sandbox_ignores_caller_api_key(self): """E2BSandbox reads API key from env only, not options.""" - source = (ROOT / "openspace" / "grounding" / "core" / "security" / "e2b_sandbox.py").read_text() + source = (ROOT / "scion" / "grounding" / "core" / "security" / "e2b_sandbox.py").read_text() # Must NOT contain options.get("api_key") assert 'options.get("api_key")' not in source, "E2BSandbox must not accept api_key from caller options" # Must use os.environ.get("E2B_API_KEY") @@ -116,7 +116,7 @@ def test_e2b_sandbox_ignores_caller_api_key(self): def test_trusted_sandbox_options_strips_api_key(self): """_build_trusted_sandbox_options strips api_key from caller input.""" - from openspace.grounding.backends.mcp.config import _build_trusted_sandbox_options + from scion.grounding.backends.mcp.config import _build_trusted_sandbox_options caller_options = { "api_key": "EVIL_INJECTED_KEY", @@ -132,7 +132,7 @@ def test_trusted_sandbox_options_strips_api_key(self): def test_trusted_sandbox_options_with_none_input(self): """_build_trusted_sandbox_options handles None caller options.""" - from openspace.grounding.backends.mcp.config import _build_trusted_sandbox_options + from scion.grounding.backends.mcp.config import _build_trusted_sandbox_options result = _build_trusted_sandbox_options(None, 30.0, 300.0) assert result["timeout"] == 30.0 @@ -150,7 +150,7 @@ class TestFailClosedBehavior: @pytest.mark.asyncio async def test_unsandboxed_stdio_denied_by_default(self): """Stdio without sandbox raises RuntimeError.""" - from openspace.grounding.backends.mcp.config import create_connector_from_config + from scion.grounding.backends.mcp.config import create_connector_from_config config = {"command": "python", "args": ["-m", "some_server"]} with patch.dict(os.environ, {}, clear=False): @@ -167,8 +167,8 @@ async def test_unsandboxed_stdio_denied_by_default(self): @pytest.mark.asyncio async def test_unsandboxed_stdio_allowed_with_explicit_opt_out(self): """Stdio without sandbox works only with OPENSPACE_ALLOW_UNSANDBOXED=1.""" - from openspace.grounding.backends.mcp.config import create_connector_from_config - from openspace.grounding.backends.mcp.transport.connectors import StdioConnector + from scion.grounding.backends.mcp.config import create_connector_from_config + from scion.grounding.backends.mcp.transport.connectors import StdioConnector config = {"command": "python", "args": ["-m", "some_server"]} with patch.dict(os.environ, {"OPENSPACE_ALLOW_UNSANDBOXED": "1"}): @@ -183,7 +183,7 @@ async def test_unsandboxed_stdio_allowed_with_explicit_opt_out(self): @pytest.mark.asyncio async def test_unsandboxed_partial_value_still_denied(self): """OPENSPACE_ALLOW_UNSANDBOXED must be exactly '1'.""" - from openspace.grounding.backends.mcp.config import create_connector_from_config + from scion.grounding.backends.mcp.config import create_connector_from_config config = {"command": "python", "args": ["-m", "some_server"]} for bad_value in ["true", "yes", "0", "", " "]: @@ -199,8 +199,8 @@ async def test_unsandboxed_partial_value_still_denied(self): @pytest.mark.asyncio async def test_sandbox_required_but_e2b_unavailable_raises(self): """When sandbox=True but E2B not installed, raises ImportError.""" - from openspace.grounding.backends.mcp import config as cfg_module - from openspace.grounding.backends.mcp.config import create_connector_from_config as _create + from scion.grounding.backends.mcp import config as cfg_module + from scion.grounding.backends.mcp.config import create_connector_from_config as _create config = {"command": "python", "args": ["-m", "some_server"]} original = cfg_module.E2B_AVAILABLE @@ -218,7 +218,7 @@ async def test_sandbox_required_but_e2b_unavailable_raises(self): def test_config_loader_fails_closed_on_invalid_config(self): """Config loader raises on validation failure, not silently defaults.""" - source = (ROOT / "openspace" / "config" / "loader.py").read_text() + source = (ROOT / "scion" / "config" / "loader.py").read_text() # Must NOT contain "using default configuration" assert "using default configuration" not in source, "Config loader must not silently fall back to defaults" # Must raise RuntimeError on validation failure @@ -226,7 +226,7 @@ def test_config_loader_fails_closed_on_invalid_config(self): def test_security_config_is_critical(self): """Security config file is loaded with critical=True.""" - source = (ROOT / "openspace" / "config" / "loader.py").read_text() + source = (ROOT / "scion" / "config" / "loader.py").read_text() assert "CONFIG_SECURITY" in source assert "_CRITICAL_CONFIG_FILES" in source assert "critical_files" in source, "Config loader must pass critical_files for security config" @@ -234,7 +234,7 @@ def test_security_config_is_critical(self): @pytest.mark.asyncio async def test_sandbox_enforcement_before_ensure_dependencies(self): """Sandbox enforcement must happen BEFORE ensure_dependencies.""" - source = (ROOT / "openspace" / "grounding" / "backends" / "mcp" / "config.py").read_text() + source = (ROOT / "scion" / "grounding" / "backends" / "mcp" / "config.py").read_text() # Find positions of key operations sandbox_check_pos = source.find("Sandbox enforcement BEFORE") deps_check_pos = source.find("ensure_dependencies") @@ -245,7 +245,7 @@ async def test_sandbox_enforcement_before_ensure_dependencies(self): @pytest.mark.asyncio async def test_unsandboxed_denied_before_deps_installed(self): """Unsandboxed stdio denied before any host-side install runs.""" - from openspace.grounding.backends.mcp.config import create_connector_from_config + from scion.grounding.backends.mcp.config import create_connector_from_config install_called = False @@ -257,7 +257,7 @@ async def mock_ensure_deps(*a, **kw): with patch.dict(os.environ, {}, clear=False): os.environ.pop("OPENSPACE_ALLOW_UNSANDBOXED", None) with patch( - "openspace.grounding.backends.mcp.installer.MCPInstallerManager.ensure_dependencies", + "scion.grounding.backends.mcp.installer.MCPInstallerManager.ensure_dependencies", side_effect=mock_ensure_deps, ): with pytest.raises(RuntimeError, match="Unsandboxed stdio execution denied"): @@ -271,7 +271,7 @@ async def mock_ensure_deps(*a, **kw): def test_no_sandbox_false_defaults_in_config_json(self): """No config JSON file ships with sandbox disabled.""" - config_dir = ROOT / "openspace" / "config" + config_dir = ROOT / "scion" / "config" for json_file in config_dir.glob("*.json"): data = json.loads(json_file.read_text()) self._check_no_sandbox_false(data, str(json_file)) @@ -298,7 +298,7 @@ class TestSandboxDocumentation: def test_env_example_documents_e2b_api_key_required(self): """E2B_API_KEY is documented as required in .env.example.""" - env_example = (ROOT / "openspace" / ".env.example").read_text() + env_example = (ROOT / "scion" / ".env.example").read_text() assert "E2B_API_KEY" in env_example # Must not say "Optional" lines_around = [line for line in env_example.splitlines() if "E2B" in line.upper() or "sandbox" in line.lower()] @@ -309,12 +309,12 @@ def test_env_example_documents_e2b_api_key_required(self): def test_env_example_documents_unsandboxed_opt_out(self): """OPENSPACE_ALLOW_UNSANDBOXED is documented.""" - env_example = (ROOT / "openspace" / ".env.example").read_text() + env_example = (ROOT / "scion" / ".env.example").read_text() assert "OPENSPACE_ALLOW_UNSANDBOXED" in env_example def test_config_readme_documents_sandbox(self): """config/README.md has E2B sandbox section.""" - readme = (ROOT / "openspace" / "config" / "README.md").read_text() + readme = (ROOT / "scion" / "config" / "README.md").read_text() assert "E2B Sandbox" in readme, "README must have E2B sandbox section" assert "E2B_API_KEY" in readme, "README must document E2B_API_KEY" assert "OPENSPACE_ALLOW_UNSANDBOXED" in readme, "README must document OPENSPACE_ALLOW_UNSANDBOXED" @@ -332,18 +332,18 @@ class TestSandboxCreationIntegration: @pytest.mark.asyncio async def test_stdio_config_creates_sandbox_connector(self): """Stdio MCP config with sandbox=True creates SandboxConnector.""" - from openspace.grounding.backends.mcp.config import create_connector_from_config - from openspace.grounding.backends.mcp.transport.connectors import SandboxConnector + from scion.grounding.backends.mcp.config import create_connector_from_config + from scion.grounding.backends.mcp.transport.connectors import SandboxConnector mock_e2b = MagicMock() config = {"command": "python", "args": ["-m", "server"]} with ( patch( - "openspace.grounding.backends.mcp.config.E2BSandbox", + "scion.grounding.backends.mcp.config.E2BSandbox", return_value=mock_e2b, ), - patch("openspace.grounding.backends.mcp.config.E2B_AVAILABLE", True), + patch("scion.grounding.backends.mcp.config.E2B_AVAILABLE", True), ): connector = await create_connector_from_config( config, @@ -356,17 +356,17 @@ async def test_stdio_config_creates_sandbox_connector(self): @pytest.mark.asyncio async def test_sandbox_connector_receives_filtered_env(self): """SandboxConnector only gets ENV_ALLOWLIST vars.""" - from openspace.grounding.backends.mcp.config import create_connector_from_config + from scion.grounding.backends.mcp.config import create_connector_from_config mock_e2b = MagicMock() config = {"command": "python", "args": ["-m", "server"]} with ( patch( - "openspace.grounding.backends.mcp.config.E2BSandbox", + "scion.grounding.backends.mcp.config.E2BSandbox", return_value=mock_e2b, ), - patch("openspace.grounding.backends.mcp.config.E2B_AVAILABLE", True), + patch("scion.grounding.backends.mcp.config.E2B_AVAILABLE", True), patch.dict( os.environ, { @@ -388,8 +388,8 @@ async def test_sandbox_connector_receives_filtered_env(self): @pytest.mark.asyncio async def test_http_config_unaffected_by_sandbox_enforcement(self): """HTTP-based MCP servers are unaffected by sandbox enforcement.""" - from openspace.grounding.backends.mcp.config import create_connector_from_config - from openspace.grounding.backends.mcp.transport.connectors import HttpConnector + from scion.grounding.backends.mcp.config import create_connector_from_config + from scion.grounding.backends.mcp.transport.connectors import HttpConnector config = {"url": "http://localhost:8080"} connector = await create_connector_from_config( @@ -403,8 +403,8 @@ async def test_http_config_unaffected_by_sandbox_enforcement(self): @pytest.mark.asyncio async def test_websocket_config_unaffected_by_sandbox_enforcement(self): """WebSocket-based MCP servers are unaffected by sandbox enforcement.""" - from openspace.grounding.backends.mcp.config import create_connector_from_config - from openspace.grounding.backends.mcp.transport.connectors import WebSocketConnector + from scion.grounding.backends.mcp.config import create_connector_from_config + from scion.grounding.backends.mcp.transport.connectors import WebSocketConnector config = {"ws_url": "ws://localhost:8080"} connector = await create_connector_from_config( @@ -418,7 +418,7 @@ async def test_websocket_config_unaffected_by_sandbox_enforcement(self): @pytest.mark.asyncio async def test_e2b_sandbox_constructed_with_trusted_options(self): """E2BSandbox receives options WITHOUT api_key from caller.""" - from openspace.grounding.backends.mcp.config import create_connector_from_config + from scion.grounding.backends.mcp.config import create_connector_from_config captured_options = {} @@ -435,10 +435,10 @@ def mock_e2b_init(options): with ( patch( - "openspace.grounding.backends.mcp.config.E2BSandbox", + "scion.grounding.backends.mcp.config.E2BSandbox", side_effect=mock_e2b_init, ), - patch("openspace.grounding.backends.mcp.config.E2B_AVAILABLE", True), + patch("scion.grounding.backends.mcp.config.E2B_AVAILABLE", True), ): await create_connector_from_config( config, @@ -461,8 +461,8 @@ class TestNoSandboxFalseInProduction: """Regression test: no production code should default sandbox to False.""" PRODUCTION_DIRS = [ - ROOT / "openspace" / "grounding" / "backends" / "mcp", - ROOT / "openspace" / "config", + ROOT / "scion" / "grounding" / "backends" / "mcp", + ROOT / "scion" / "config", ] def test_no_sandbox_false_keyword_defaults(self): diff --git a/tests/test_facade_coverage.py b/tests/test_facade_coverage.py index ebf5b00..4a80fd2 100644 --- a/tests/test_facade_coverage.py +++ b/tests/test_facade_coverage.py @@ -19,9 +19,9 @@ import pytest -from openspace.skill_engine.registry import SkillMeta -from openspace.skill_engine.store import SkillStore -from openspace.skill_engine.types import ( +from scion.skill_engine.registry import SkillMeta +from scion.skill_engine.store import SkillStore +from scion.skill_engine.types import ( ExecutionAnalysis, SkillCategory, SkillJudgment, diff --git a/tests/test_fs_broker.py b/tests/test_fs_broker.py index 1815689..d42850c 100644 --- a/tests/test_fs_broker.py +++ b/tests/test_fs_broker.py @@ -19,7 +19,7 @@ import pytest -from openspace.sandbox.fs_broker import ( +from scion.sandbox.fs_broker import ( DeniedPathError, FileSizeLimitError, FilesystemBroker, @@ -36,7 +36,7 @@ safe_open_read, safe_open_write, ) -from openspace.sandbox.leases import FilesystemCapability +from scion.sandbox.leases import FilesystemCapability _IS_WINDOWS = platform.system() == "Windows" _SUPPORTS_SYMLINKS = not _IS_WINDOWS # Conservative; some Windows configs allow them diff --git a/tests/test_integration.py b/tests/test_integration.py index d6c7360..b0619e9 100644 --- a/tests/test_integration.py +++ b/tests/test_integration.py @@ -83,8 +83,8 @@ async def send(message): def make_protected_app(inner_app=None, token=TEST_TOKEN): """Build the full middleware stack matching production wiring.""" - from openspace.auth.bearer import BearerTokenMiddleware - from openspace.auth.rate_limit import RateLimitMiddleware + from scion.auth.bearer import BearerTokenMiddleware + from scion.auth.rate_limit import RateLimitMiddleware if inner_app is None: # Simple echo app @@ -197,7 +197,7 @@ class TestSkillExecutionIntegration: @pytest.mark.asyncio async def test_execute_task_returns_formatted_result(self): """execute_task produces MCP-formatted result when engine succeeds.""" - from openspace.mcp_server import execute_task + from scion.mcp_server import execute_task mock_os = MagicMock() mock_os.is_initialized.return_value = True @@ -210,11 +210,11 @@ async def test_execute_task_returns_formatted_result(self): ) with ( - patch("openspace.mcp_server._get_openspace", new_callable=AsyncMock, return_value=mock_os), - patch("openspace.mcp_server._auto_register_skill_dirs", new_callable=AsyncMock), - patch("openspace.mcp_server._cloud_search_and_import", new_callable=AsyncMock, return_value=[]), - patch("openspace.mcp_server._format_task_result", return_value={"result": "formatted"}), - patch("openspace.mcp_server._write_upload_meta"), + patch("scion.mcp_server._get_openspace", new_callable=AsyncMock, return_value=mock_os), + patch("scion.mcp_server._auto_register_skill_dirs", new_callable=AsyncMock), + patch("scion.mcp_server._cloud_search_and_import", new_callable=AsyncMock, return_value=[]), + patch("scion.mcp_server._format_task_result", return_value={"result": "formatted"}), + patch("scion.mcp_server._write_upload_meta"), ): result = await execute_task(task="test task") mock_os.execute.assert_called_once() @@ -222,7 +222,7 @@ async def test_execute_task_returns_formatted_result(self): @pytest.mark.asyncio async def test_execute_task_auto_registers_skill_dirs(self): """execute_task calls _auto_register_skill_dirs when skill_dirs provided.""" - from openspace.mcp_server import execute_task + from scion.mcp_server import execute_task mock_os = MagicMock() mock_os.is_initialized.return_value = True @@ -235,11 +235,11 @@ async def test_execute_task_auto_registers_skill_dirs(self): ) with ( - patch("openspace.mcp_server._get_openspace", new_callable=AsyncMock, return_value=mock_os), - patch("openspace.mcp_server._auto_register_skill_dirs", new_callable=AsyncMock) as mock_register, - patch("openspace.mcp_server._cloud_search_and_import", new_callable=AsyncMock, return_value=[]), - patch("openspace.mcp_server._format_task_result", return_value={"result": "ok"}), - patch("openspace.mcp_server._write_upload_meta"), + patch("scion.mcp_server._get_openspace", new_callable=AsyncMock, return_value=mock_os), + patch("scion.mcp_server._auto_register_skill_dirs", new_callable=AsyncMock) as mock_register, + patch("scion.mcp_server._cloud_search_and_import", new_callable=AsyncMock, return_value=[]), + patch("scion.mcp_server._format_task_result", return_value={"result": "ok"}), + patch("scion.mcp_server._write_upload_meta"), ): await execute_task(task="test", skill_dirs=["/tmp/skills"]) mock_register.assert_called() @@ -247,7 +247,7 @@ async def test_execute_task_auto_registers_skill_dirs(self): @pytest.mark.asyncio async def test_execute_task_respects_search_scope_local(self): """execute_task skips cloud import when search_scope='local'.""" - from openspace.mcp_server import execute_task + from scion.mcp_server import execute_task mock_os = MagicMock() mock_os.is_initialized.return_value = True @@ -260,11 +260,11 @@ async def test_execute_task_respects_search_scope_local(self): ) with ( - patch("openspace.mcp_server._get_openspace", new_callable=AsyncMock, return_value=mock_os), - patch("openspace.mcp_server._auto_register_skill_dirs", new_callable=AsyncMock), - patch("openspace.mcp_server._cloud_search_and_import", new_callable=AsyncMock) as mock_import, - patch("openspace.mcp_server._format_task_result", return_value={"result": "ok"}), - patch("openspace.mcp_server._write_upload_meta"), + patch("scion.mcp_server._get_openspace", new_callable=AsyncMock, return_value=mock_os), + patch("scion.mcp_server._auto_register_skill_dirs", new_callable=AsyncMock), + patch("scion.mcp_server._cloud_search_and_import", new_callable=AsyncMock) as mock_import, + patch("scion.mcp_server._format_task_result", return_value={"result": "ok"}), + patch("scion.mcp_server._write_upload_meta"), ): await execute_task(task="test", search_scope="local") mock_import.assert_not_called() @@ -357,16 +357,16 @@ async def test_error_responses_contain_no_tracebacks(self): @pytest.mark.asyncio async def test_execute_task_exception_returns_safe_error(self): """Internal exception in execute_task returns safe error, no traceback.""" - from openspace.mcp_server import execute_task + from scion.mcp_server import execute_task mock_os = MagicMock() mock_os.is_initialized.return_value = True mock_os.execute = AsyncMock(side_effect=RuntimeError("internal boom")) with ( - patch("openspace.mcp_server._get_openspace", new_callable=AsyncMock, return_value=mock_os), - patch("openspace.mcp_server._auto_register_skill_dirs", new_callable=AsyncMock), - patch("openspace.mcp_server._cloud_search_and_import", new_callable=AsyncMock), + patch("scion.mcp_server._get_openspace", new_callable=AsyncMock, return_value=mock_os), + patch("scion.mcp_server._auto_register_skill_dirs", new_callable=AsyncMock), + patch("scion.mcp_server._cloud_search_and_import", new_callable=AsyncMock), ): result = await execute_task(task="crash test") result_str = str(result) diff --git a/tests/test_lineage_tracker.py b/tests/test_lineage_tracker.py index fad3ba8..e718aaf 100644 --- a/tests/test_lineage_tracker.py +++ b/tests/test_lineage_tracker.py @@ -16,7 +16,7 @@ import pytest -from openspace.skill_engine.types import ( +from scion.skill_engine.types import ( SkillCategory, SkillLineage, SkillOrigin, @@ -57,7 +57,7 @@ def _make_record( @pytest.fixture def tracker(tmp_path: Path): """Create a LineageTracker backed by a temp SQLite database.""" - from openspace.skill_engine.lineage_tracker import LineageTracker + from scion.skill_engine.lineage_tracker import LineageTracker db_path = tmp_path / "test_lineage.db" lt = LineageTracker(db_path=db_path) @@ -68,7 +68,7 @@ def tracker(tmp_path: Path): @pytest.fixture def seeded_tracker(tracker): """Tracker pre-seeded with a 3-generation chain: root → child → grandchild.""" - from openspace.skill_engine.lineage_tracker import LineageTracker + from scion.skill_engine.lineage_tracker import LineageTracker lt: LineageTracker = tracker @@ -379,8 +379,8 @@ def test_shared_conn_injection(self, tmp_path): import sqlite3 import threading - from openspace.skill_engine.lineage_tracker import LineageTracker - from openspace.skill_engine.skill_repository import SkillRepository + from scion.skill_engine.lineage_tracker import LineageTracker + from scion.skill_engine.skill_repository import SkillRepository db_path = tmp_path / "shared.db" conn = sqlite3.connect(str(db_path), check_same_thread=False) @@ -503,8 +503,8 @@ def test_shared_conn_read_isolation(self, tmp_path): import sqlite3 import threading - from openspace.skill_engine.lineage_tracker import LineageTracker - from openspace.skill_engine.skill_repository import SkillRepository + from scion.skill_engine.lineage_tracker import LineageTracker + from scion.skill_engine.skill_repository import SkillRepository db_path = tmp_path / "isolation_test.db" @@ -544,7 +544,7 @@ class TestSkillStoreFacade: def test_find_children_facade(self, tmp_path): """Test SkillStore.find_children delegates properly.""" - from openspace.skill_engine.store import SkillStore + from scion.skill_engine.store import SkillStore store = SkillStore(db_path=tmp_path / "facade_test.db") @@ -565,7 +565,7 @@ def test_find_children_facade(self, tmp_path): def test_get_versions_facade_hydration(self, tmp_path): """Test SkillStore.get_versions returns fully hydrated records.""" - from openspace.skill_engine.store import SkillStore + from scion.skill_engine.store import SkillStore store = SkillStore(db_path=tmp_path / "hydration_test.db") @@ -591,7 +591,7 @@ def test_get_versions_facade_hydration(self, tmp_path): def test_get_ancestry_facade_hydration(self, tmp_path): """Test SkillStore.get_ancestry returns fully hydrated records.""" - from openspace.skill_engine.store import SkillStore + from scion.skill_engine.store import SkillStore store = SkillStore(db_path=tmp_path / "ancestry_test.db") diff --git a/tests/test_mcp_auth.py b/tests/test_mcp_auth.py index ed4847e..5d2f21a 100644 --- a/tests/test_mcp_auth.py +++ b/tests/test_mcp_auth.py @@ -18,7 +18,7 @@ import pytest -from openspace.auth.bearer import ( +from scion.auth.bearer import ( BEARER_TOKEN_ENV, MIN_TOKEN_LENGTH, BearerTokenMiddleware, @@ -219,7 +219,7 @@ def test_sse_without_token_exits(self, monkeypatch): monkeypatch.delenv(BEARER_TOKEN_ENV, raising=False) monkeypatch.setattr(sys, "argv", ["openspace-mcp", "--transport", "sse"]) with pytest.raises(SystemExit) as exc_info: - from openspace.mcp_server import run_mcp_server + from scion.mcp_server import run_mcp_server run_mcp_server() assert exc_info.value.code == 1 @@ -228,7 +228,7 @@ def test_sse_with_weak_token_exits(self, monkeypatch): monkeypatch.setenv(BEARER_TOKEN_ENV, "tooshort") monkeypatch.setattr(sys, "argv", ["openspace-mcp", "--transport", "sse"]) with pytest.raises(SystemExit) as exc_info: - from openspace.mcp_server import run_mcp_server + from scion.mcp_server import run_mcp_server run_mcp_server() assert exc_info.value.code == 1 @@ -241,7 +241,7 @@ def test_streamable_http_without_token_exits(self, monkeypatch): ["openspace-mcp", "--transport", "streamable-http"], ) with pytest.raises(SystemExit) as exc_info: - from openspace.mcp_server import run_mcp_server + from scion.mcp_server import run_mcp_server run_mcp_server() assert exc_info.value.code == 1 diff --git a/tests/test_migration_manager.py b/tests/test_migration_manager.py index 3ad8149..5abce39 100644 --- a/tests/test_migration_manager.py +++ b/tests/test_migration_manager.py @@ -5,7 +5,7 @@ from pathlib import Path from unittest.mock import patch -from openspace.skill_engine.migration_manager import MigrationManager, CURRENT_VERSION +from scion.skill_engine.migration_manager import MigrationManager, CURRENT_VERSION @pytest.fixture @@ -283,7 +283,7 @@ def test_ensure_current_schema_handles_newer_version(self, migration_manager): migration_manager.set_schema_version(5) # Should log warning but not fail - with patch('openspace.skill_engine.migration_manager.logger') as mock_logger: + with patch('scion.skill_engine.migration_manager.logger') as mock_logger: migration_manager.ensure_current_schema(3) mock_logger.warning.assert_called_once_with( "Database schema version 5 is newer than expected 3" @@ -371,7 +371,7 @@ class TestSkillStoreFacadeIntegration: def test_skill_store_initializes_with_migration_manager(self, temp_db_path): """Test that SkillStore creates and uses MigrationManager.""" - from openspace.skill_engine.store import SkillStore + from scion.skill_engine.store import SkillStore store = SkillStore(db_path=temp_db_path) @@ -386,7 +386,7 @@ def test_skill_store_initializes_with_migration_manager(self, temp_db_path): def test_skill_store_facade_methods(self, temp_db_path): """Test that SkillStore facade methods work correctly.""" - from openspace.skill_engine.store import SkillStore + from scion.skill_engine.store import SkillStore store = SkillStore(db_path=temp_db_path) @@ -444,7 +444,7 @@ def test_migration_atomic_on_failure(self, migration_manager): assert migration_manager.get_schema_version() == 0 # Patch the DDL statements to include a failing statement - with patch('openspace.skill_engine.migration_manager._DDL_STATEMENTS') as mock_statements: + with patch('scion.skill_engine.migration_manager._DDL_STATEMENTS') as mock_statements: mock_statements.__iter__ = lambda x: iter([ "CREATE TABLE test_table (id INTEGER PRIMARY KEY)", "INVALID SQL THAT WILL FAIL", # This will cause the migration to fail @@ -476,9 +476,9 @@ class TestDDLSingleSourceOfTruth: def test_ddl_single_source_of_truth(self): """Verify no other module has CREATE TABLE strings (architecture invariant).""" - import openspace.skill_engine.skill_repository as repo_module - import openspace.skill_engine.analysis_store as analysis_module - import openspace.skill_engine.tag_search as tag_module + import scion.skill_engine.skill_repository as repo_module + import scion.skill_engine.analysis_store as analysis_module + import scion.skill_engine.tag_search as tag_module import inspect # Check module source code for CREATE TABLE strings @@ -495,9 +495,9 @@ def test_ddl_single_source_of_truth(self): def test_standalone_modules_use_migration_manager(self, temp_db_path): """Verify SkillRepository/AnalysisStore/TagSearch standalone mode delegates DDL to MigrationManager.""" - from openspace.skill_engine.skill_repository import SkillRepository - from openspace.skill_engine.analysis_store import AnalysisStore - from openspace.skill_engine.tag_search import TagSearch + from scion.skill_engine.skill_repository import SkillRepository + from scion.skill_engine.analysis_store import AnalysisStore + from scion.skill_engine.tag_search import TagSearch # Each module should create schema via MigrationManager in standalone mode modules = [ @@ -528,9 +528,9 @@ def test_standalone_modules_use_migration_manager(self, temp_db_path): def test_schema_consistency_across_modules(self, temp_db_path): """Verify that all modules create identical schemas.""" - from openspace.skill_engine.skill_repository import SkillRepository - from openspace.skill_engine.analysis_store import AnalysisStore - from openspace.skill_engine.tag_search import TagSearch + from scion.skill_engine.skill_repository import SkillRepository + from scion.skill_engine.analysis_store import AnalysisStore + from scion.skill_engine.tag_search import TagSearch modules = [SkillRepository, AnalysisStore, TagSearch] schemas = [] diff --git a/tests/test_net_proxy.py b/tests/test_net_proxy.py index d029465..33552b8 100644 --- a/tests/test_net_proxy.py +++ b/tests/test_net_proxy.py @@ -13,8 +13,8 @@ import pytest -from openspace.sandbox.leases import NetworkCapability -from openspace.sandbox.net_proxy import ( +from scion.sandbox.leases import NetworkCapability +from scion.sandbox.net_proxy import ( ConnectionLimitError, ConnectionNotFoundError, ConnectionTracker, @@ -496,7 +496,7 @@ def test_t0_schema_rejects_nonempty_allowed_domains(self) -> None: """LeaseSchema must reject T0 with non-empty allowed_domains.""" from pydantic import ValidationError - from openspace.sandbox.leases import LeaseSchema, TrustTier + from scion.sandbox.leases import LeaseSchema, TrustTier with pytest.raises(ValidationError, match="allowed_domains"): LeaseSchema( @@ -513,7 +513,7 @@ def test_t1_schema_rejects_nonempty_allowed_domains(self) -> None: """LeaseSchema must reject T1 with non-empty allowed_domains.""" from pydantic import ValidationError - from openspace.sandbox.leases import LeaseSchema, TrustTier + from scion.sandbox.leases import LeaseSchema, TrustTier with pytest.raises(ValidationError, match="allowed_domains"): LeaseSchema( diff --git a/tests/test_p3_integration.py b/tests/test_p3_integration.py index 631bc70..5375630 100644 --- a/tests/test_p3_integration.py +++ b/tests/test_p3_integration.py @@ -24,8 +24,8 @@ import pytest -from openspace.skill_engine.store import SkillStore -from openspace.skill_engine.types import ( +from scion.skill_engine.store import SkillStore +from scion.skill_engine.types import ( EvolutionSuggestion, EvolutionType, ExecutionAnalysis, diff --git a/tests/test_process_broker.py b/tests/test_process_broker.py index 3c4436e..880f152 100644 --- a/tests/test_process_broker.py +++ b/tests/test_process_broker.py @@ -1,4 +1,4 @@ -"""Tests for openspace.sandbox.process_broker — EPIC 2.4. +"""Tests for scion.sandbox.process_broker — EPIC 2.4. Covers: - #99: Command allow/deny enforcement @@ -14,8 +14,8 @@ import pytest -from openspace.sandbox.leases import REQUIRED_BLOCKED_COMMANDS, ProcessCapability -from openspace.sandbox.process_broker import ( +from scion.sandbox.leases import REQUIRED_BLOCKED_COMMANDS, ProcessCapability +from scion.sandbox.process_broker import ( _DANGEROUS_SYSCALLS, _LINK_COMMANDS, _SHELL_BINARIES, diff --git a/tests/test_rate_limit.py b/tests/test_rate_limit.py index 35090fb..66230fc 100644 --- a/tests/test_rate_limit.py +++ b/tests/test_rate_limit.py @@ -20,7 +20,7 @@ import pytest -from openspace.auth.rate_limit import ( +from scion.auth.rate_limit import ( RATE_LIMIT_PER_IP_ENV, RATE_LIMIT_PER_TOKEN_ENV, RATE_LIMIT_WINDOW_ENV, diff --git a/tests/test_sdk_contract.py b/tests/test_sdk_contract.py index 84a988c..5cd5164 100644 --- a/tests/test_sdk_contract.py +++ b/tests/test_sdk_contract.py @@ -20,7 +20,7 @@ from typing import Any, Optional # --------------------------------------------------------------------------- -# SDK envelope helpers (will become openspace.sdk.envelope in Phase 6) +# SDK envelope helpers (will become scion.sdk.envelope in Phase 6) # --------------------------------------------------------------------------- @@ -57,7 +57,7 @@ def failure(cls, code: str, message: str, request_id: str = "", **details: Any) # --------------------------------------------------------------------------- -# SDK request/response types (will become openspace.sdk.types in Phase 6) +# SDK request/response types (will become scion.sdk.types in Phase 6) # --------------------------------------------------------------------------- diff --git a/tests/test_secret_broker.py b/tests/test_secret_broker.py index 46f5314..b451e7f 100644 --- a/tests/test_secret_broker.py +++ b/tests/test_secret_broker.py @@ -1,4 +1,4 @@ -"""Tests for openspace.secret.broker — EPIC 2.6. +"""Tests for scion.secret.broker — EPIC 2.6. Covers: - #52: SecretBrokerPort concrete implementation @@ -16,8 +16,8 @@ import pytest -from openspace.sandbox.leases import SecretCapability -from openspace.secret.broker import ( +from scion.sandbox.leases import SecretCapability +from scion.secret.broker import ( SecretAccessDenied, SecretBroker, SecretBrokerError, diff --git a/tests/test_secret_isolation.py b/tests/test_secret_isolation.py index 1640450..6566691 100644 --- a/tests/test_secret_isolation.py +++ b/tests/test_secret_isolation.py @@ -13,13 +13,13 @@ import pytest -from openspace.security import check_code_safety -from openspace.security.ast_scanner import ( +from scion.security import check_code_safety +from scion.security.ast_scanner import ( Severity, load_blocklist, scan_code, ) -from openspace.security.env_filter import ( +from scion.security.env_filter import ( ENV_ALLOWLIST, get_safe_env, is_sensitive_key, @@ -309,7 +309,7 @@ def test_sandbox_connector_filters_env(self): """SandboxConnector must strip secrets from env before passing to sandbox.""" from unittest.mock import MagicMock - from openspace.grounding.backends.mcp.transport.connectors.sandbox import SandboxConnector + from scion.grounding.backends.mcp.transport.connectors.sandbox import SandboxConnector mock_sandbox = MagicMock() hostile_env = { diff --git a/tests/test_skill_repository.py b/tests/test_skill_repository.py index 1a654d1..00e2357 100644 --- a/tests/test_skill_repository.py +++ b/tests/test_skill_repository.py @@ -19,7 +19,7 @@ import pytest -from openspace.skill_engine.types import ( +from scion.skill_engine.types import ( SkillCategory, SkillLineage, SkillOrigin, @@ -54,7 +54,7 @@ def _make_record( @pytest.fixture def repo(tmp_path: Path): """Create a SkillRepository backed by a temp SQLite database.""" - from openspace.skill_engine.skill_repository import SkillRepository + from scion.skill_engine.skill_repository import SkillRepository db_path = tmp_path / "test_repo.db" repository = SkillRepository(db_path=db_path) @@ -399,7 +399,7 @@ class TestSharedLock: """When given an external lock, SkillRepository should use it.""" def test_uses_provided_lock(self, tmp_path): - from openspace.skill_engine.skill_repository import SkillRepository + from scion.skill_engine.skill_repository import SkillRepository import threading external_lock = threading.Lock() @@ -409,7 +409,7 @@ def test_uses_provided_lock(self, tmp_path): repo.close() def test_creates_own_lock_when_none_provided(self, tmp_path): - from openspace.skill_engine.skill_repository import SkillRepository + from scion.skill_engine.skill_repository import SkillRepository import threading db_path = tmp_path / "own_lock.db" @@ -419,7 +419,7 @@ def test_creates_own_lock_when_none_provided(self, tmp_path): def test_shared_conn_and_lock(self, tmp_path): """Shared connection + shared lock should work without deadlock.""" - from openspace.skill_engine.skill_repository import SkillRepository + from scion.skill_engine.skill_repository import SkillRepository import sqlite3 import threading diff --git a/tests/test_skill_types.py b/tests/test_skill_types.py index e579e94..839b876 100644 --- a/tests/test_skill_types.py +++ b/tests/test_skill_types.py @@ -16,7 +16,7 @@ import pytest -from openspace.skill_engine.types import ( +from scion.skill_engine.types import ( EvolutionSuggestion, EvolutionType, ExecutionAnalysis, diff --git a/tests/test_structured_logging.py b/tests/test_structured_logging.py index 3930ba4..0f4fd7b 100644 --- a/tests/test_structured_logging.py +++ b/tests/test_structured_logging.py @@ -25,17 +25,17 @@ class TestContextVariables: """bind_context / clear_context / get_context work correctly.""" def setup_method(self): - from openspace.domain.logging import clear_context + from scion.domain.logging import clear_context clear_context() def teardown_method(self): - from openspace.domain.logging import clear_context + from scion.domain.logging import clear_context clear_context() def test_bind_and_get_context(self): - from openspace.domain.logging import bind_context, get_context + from scion.domain.logging import bind_context, get_context bind_context(task_id="t-42", correlation_id="abc123") ctx = get_context() @@ -44,28 +44,28 @@ def test_bind_and_get_context(self): assert "session_id" not in ctx # Empty values excluded def test_clear_context(self): - from openspace.domain.logging import bind_context, clear_context, get_context + from scion.domain.logging import bind_context, clear_context, get_context bind_context(task_id="t-1", session_id="s-1") clear_context() assert get_context() == {} def test_bind_unknown_key_ignored(self): - from openspace.domain.logging import bind_context, get_context + from scion.domain.logging import bind_context, get_context bind_context(task_id="t-1", unknown_field="ignored") ctx = get_context() assert ctx == {"task_id": "t-1"} def test_bind_overwrites_previous(self): - from openspace.domain.logging import bind_context, get_context + from scion.domain.logging import bind_context, get_context bind_context(task_id="t-1") bind_context(task_id="t-2") assert get_context()["task_id"] == "t-2" def test_all_four_context_vars(self): - from openspace.domain.logging import bind_context, get_context + from scion.domain.logging import bind_context, get_context bind_context( task_id="t-1", @@ -85,18 +85,18 @@ class TestContextIsolation: """Context vars are isolated across async tasks.""" def setup_method(self): - from openspace.domain.logging import clear_context + from scion.domain.logging import clear_context clear_context() def teardown_method(self): - from openspace.domain.logging import clear_context + from scion.domain.logging import clear_context clear_context() @pytest.mark.asyncio async def test_async_task_isolation(self): - from openspace.domain.logging import bind_context, get_context + from scion.domain.logging import bind_context, get_context results = {} @@ -124,28 +124,28 @@ class TestRedaction: """Sensitive data is redacted in log events.""" def test_redacts_api_key(self): - from openspace.domain.logging import _redact_sensitive + from scion.domain.logging import _redact_sensitive event = {"event": "test", "api_key": "sk-secret-123"} result = _redact_sensitive(None, "info", event) assert result["api_key"] == "***REDACTED***" def test_redacts_token_field(self): - from openspace.domain.logging import _redact_sensitive + from scion.domain.logging import _redact_sensitive event = {"event": "test", "bearer_token": "eyJ..."} result = _redact_sensitive(None, "info", event) assert result["bearer_token"] == "***REDACTED***" def test_redacts_password(self): - from openspace.domain.logging import _redact_sensitive + from scion.domain.logging import _redact_sensitive event = {"event": "test", "password": "hunter2"} result = _redact_sensitive(None, "info", event) assert result["password"] == "***REDACTED***" def test_redacts_key_suffix_match(self): - from openspace.domain.logging import _redact_sensitive + from scion.domain.logging import _redact_sensitive event = {"event": "test", "my_api_key": "secret", "client_secret": "s3cret"} result = _redact_sensitive(None, "info", event) @@ -153,7 +153,7 @@ def test_redacts_key_suffix_match(self): assert result["client_secret"] == "***REDACTED***" def test_no_false_positive_on_non_sensitive(self): - from openspace.domain.logging import _redact_sensitive + from scion.domain.logging import _redact_sensitive # These should NOT be redacted despite containing "key"/"token" substrings event = { @@ -168,7 +168,7 @@ def test_no_false_positive_on_non_sensitive(self): assert result["monkey_patch"] is True def test_redacts_nested_dict_sensitive_keys(self): - from openspace.domain.logging import _redact_sensitive + from scion.domain.logging import _redact_sensitive event = { "event": "api_call", @@ -179,7 +179,7 @@ def test_redacts_nested_dict_sensitive_keys(self): assert result["payload"]["user_id"] == "u-42" def test_redacts_deeply_nested(self): - from openspace.domain.logging import _redact_sensitive + from scion.domain.logging import _redact_sensitive event = { "event": "test", @@ -190,7 +190,7 @@ def test_redacts_deeply_nested(self): def test_redacts_top_level_list_with_sensitive_dicts(self): """Top-level list/tuple fields containing dicts with sensitive keys.""" - from openspace.domain.logging import _redact_sensitive + from scion.domain.logging import _redact_sensitive event = { "event": "test", @@ -202,7 +202,7 @@ def test_redacts_top_level_list_with_sensitive_dicts(self): def test_redacts_top_level_tuple_with_sensitive_dicts(self): """Tuple variant of top-level list redaction.""" - from openspace.domain.logging import _redact_sensitive + from scion.domain.logging import _redact_sensitive event = { "event": "test", @@ -213,7 +213,7 @@ def test_redacts_top_level_tuple_with_sensitive_dicts(self): def test_redacts_camel_case_keys(self): """camelCase keys like apiKey, accessToken are normalized and redacted.""" - from openspace.domain.logging import _redact_sensitive + from scion.domain.logging import _redact_sensitive event = { "event": "test", @@ -230,7 +230,7 @@ def test_redacts_camel_case_keys(self): def test_redacts_pascal_case_keys(self): """PascalCase keys are also normalized and redacted.""" - from openspace.domain.logging import _redact_sensitive + from scion.domain.logging import _redact_sensitive event = {"event": "test", "ApiKey": "key1", "AuthToken": "tok1"} result = _redact_sensitive(None, "info", event) @@ -239,7 +239,7 @@ def test_redacts_pascal_case_keys(self): def test_camel_case_not_false_positive(self): """camelCase keys that aren't sensitive should NOT be redacted.""" - from openspace.domain.logging import _redact_sensitive + from scion.domain.logging import _redact_sensitive event = {"event": "test", "tokenCount": 42, "keyboardLayout": "us"} result = _redact_sensitive(None, "info", event) @@ -248,7 +248,7 @@ def test_camel_case_not_false_positive(self): def test_recursion_depth_limit(self): """Deeply nested payloads stop redacting at _MAX_REDACT_DEPTH, no RecursionError.""" - from openspace.domain.logging import _MAX_REDACT_DEPTH, _redact_value + from scion.domain.logging import _MAX_REDACT_DEPTH, _redact_value # Build a structure deeper than the limit nested: dict = {"api_key": "leak-at-depth"} @@ -269,7 +269,7 @@ def test_recursion_depth_limit(self): assert result2["wrapper"]["api_key"] == "***REDACTED***" def test_preserves_non_sensitive(self): - from openspace.domain.logging import _redact_sensitive + from scion.domain.logging import _redact_sensitive event = {"event": "test", "task_id": "t-42", "status": "ok"} result = _redact_sensitive(None, "info", event) @@ -277,7 +277,7 @@ def test_preserves_non_sensitive(self): assert result["status"] == "ok" def test_truncates_long_values(self): - from openspace.domain.logging import _redact_sensitive + from scion.domain.logging import _redact_sensitive long_value = "x" * 2000 event = {"event": "test", "output": long_value} @@ -295,17 +295,17 @@ class TestLoggerCreation: """get_logger and configure_logging work correctly.""" def setup_method(self): - from openspace.domain.logging import reset_logging + from scion.domain.logging import reset_logging reset_logging() def teardown_method(self): - from openspace.domain.logging import reset_logging + from scion.domain.logging import reset_logging reset_logging() def test_get_logger_returns_bound_logger(self): - from openspace.domain.logging import get_logger + from scion.domain.logging import get_logger log = get_logger("test.module") assert log is not None @@ -315,27 +315,27 @@ def test_get_logger_returns_bound_logger(self): assert hasattr(log, "debug") def test_get_logger_default_name(self): - from openspace.domain.logging import get_logger + from scion.domain.logging import get_logger log = get_logger() assert log is not None def test_configure_idempotent(self): - from openspace.domain.logging import configure_logging + from scion.domain.logging import configure_logging configure_logging(level=logging.DEBUG) configure_logging(level=logging.WARNING) # Should be no-op # No exception = pass def test_configure_json_output(self): - from openspace.domain.logging import configure_logging, reset_logging + from scion.domain.logging import configure_logging, reset_logging reset_logging() configure_logging(json_output=True) # No exception = pass def test_configure_no_colors(self): - from openspace.domain.logging import configure_logging, reset_logging + from scion.domain.logging import configure_logging, reset_logging reset_logging() configure_logging(colors=False) @@ -351,19 +351,19 @@ class TestIntegration: """Structured logging integrates with existing code patterns.""" def setup_method(self): - from openspace.domain.logging import clear_context, reset_logging + from scion.domain.logging import clear_context, reset_logging reset_logging() clear_context() def teardown_method(self): - from openspace.domain.logging import clear_context, reset_logging + from scion.domain.logging import clear_context, reset_logging reset_logging() clear_context() def test_structlog_event_includes_context(self, capsys): - from openspace.domain.logging import ( + from scion.domain.logging import ( bind_context, configure_logging, get_logger, @@ -380,12 +380,12 @@ def test_structlog_event_includes_context(self, capsys): def test_stdlib_logger_still_works(self): """stdlib loggers produce output and go through shared processors.""" - from openspace.domain.logging import bind_context, configure_logging + from scion.domain.logging import bind_context, configure_logging configure_logging(level=logging.DEBUG, colors=False) bind_context(task_id="stdlib-t1") - stdlib_logger = logging.getLogger("openspace.test_stdlib_bridge") + stdlib_logger = logging.getLogger("scion.test_stdlib_bridge") # Capture output from the root handler import io @@ -406,7 +406,7 @@ def test_stdlib_logger_still_works(self): root.removeHandler(handler) def test_context_vars_processor_injects(self): - from openspace.domain.logging import _inject_context_vars, bind_context + from scion.domain.logging import _inject_context_vars, bind_context bind_context(task_id="t-1", correlation_id="c-1") event: dict = {"event": "test"} @@ -415,7 +415,7 @@ def test_context_vars_processor_injects(self): assert result["correlation_id"] == "c-1" def test_context_vars_dont_overwrite_explicit(self): - from openspace.domain.logging import _inject_context_vars, bind_context + from scion.domain.logging import _inject_context_vars, bind_context bind_context(task_id="t-1") event: dict = {"event": "test", "task_id": "explicit-id"} @@ -423,7 +423,7 @@ def test_context_vars_dont_overwrite_explicit(self): assert result["task_id"] == "explicit-id" # Explicit wins def test_reset_allows_reconfigure(self): - from openspace.domain.logging import ( + from scion.domain.logging import ( configure_logging, reset_logging, ) diff --git a/tests/test_tag_search.py b/tests/test_tag_search.py index 8183c2b..68977fe 100644 --- a/tests/test_tag_search.py +++ b/tests/test_tag_search.py @@ -20,7 +20,7 @@ import pytest -from openspace.skill_engine.types import ( +from scion.skill_engine.types import ( SkillCategory, SkillLineage, SkillOrigin, @@ -61,7 +61,7 @@ def _make_record( @pytest.fixture def tag_search(tmp_path: Path): """Create a TagSearch backed by a temp SQLite database.""" - from openspace.skill_engine.tag_search import TagSearch + from scion.skill_engine.tag_search import TagSearch db_path = tmp_path / "test_tagsearch.db" ts = TagSearch(db_path=db_path) @@ -72,7 +72,7 @@ def tag_search(tmp_path: Path): @pytest.fixture def populated_tag_search(tag_search): """TagSearch with some test data.""" - from openspace.skill_engine.skill_repository import SkillRepository + from scion.skill_engine.skill_repository import SkillRepository # We need to use SkillRepository to populate records, then TagSearch for tags repo = SkillRepository(db_path=tag_search._db_path) @@ -439,7 +439,7 @@ class TestSharedConnection: def test_shared_connection_and_lock(self, tmp_path: Path): """TagSearch should work when sharing connection with another component.""" - from openspace.skill_engine.tag_search import TagSearch + from scion.skill_engine.tag_search import TagSearch # Create a shared connection and lock db_path = tmp_path / "shared.db" @@ -493,7 +493,7 @@ def test_shared_connection_and_lock(self, tmp_path: Path): def test_reader_with_shared_connection_acquires_lock(self, tmp_path: Path): """When using shared connection, _reader() should acquire lock.""" - from openspace.skill_engine.tag_search import TagSearch + from scion.skill_engine.tag_search import TagSearch db_path = tmp_path / "shared.db" conn = sqlite3.connect(str(db_path), check_same_thread=False) @@ -525,14 +525,14 @@ def test_tag_search_closed_operations_fail(self, tag_search): tag_search.get_tags("skill1") def test_neither_db_path_nor_conn_raises(self): - from openspace.skill_engine.tag_search import TagSearch + from scion.skill_engine.tag_search import TagSearch with pytest.raises(ValueError, match="Either db_path or conn must be provided"): TagSearch() def test_db_retry_on_operational_error(self, tmp_path: Path): """_db_retry should handle transient SQLite errors.""" - from openspace.skill_engine.tag_search import TagSearch + from scion.skill_engine.tag_search import TagSearch # Create a TagSearch with actual database db_path = tmp_path / "test.db" @@ -563,7 +563,7 @@ class TestSkillStoreFacadeIntegration: def test_skill_store_delegates_to_tag_search(self, tmp_path: Path): """SkillStore should delegate tag/search operations to TagSearch.""" - from openspace.skill_engine.store import SkillStore + from scion.skill_engine.store import SkillStore # Create SkillStore (which should initialize TagSearch internally) store = SkillStore(db_path=tmp_path / "integration.db") @@ -582,7 +582,7 @@ def test_skill_store_delegates_to_tag_search(self, tmp_path: Path): def test_skill_store_tag_sync_delegation(self, tmp_path: Path): """SkillStore should delegate tag sync during record saving.""" import asyncio - from openspace.skill_engine.store import SkillStore + from scion.skill_engine.store import SkillStore store = SkillStore(db_path=tmp_path / "integration.db") @@ -671,8 +671,8 @@ class TestSkillStoreFacadeCompleteness: def test_skill_store_facade_completeness(self, tmp_path: Path): """Verify ALL TagSearch public methods are accessible through SkillStore.""" - from openspace.skill_engine.store import SkillStore - from openspace.skill_engine.tag_search import TagSearch + from scion.skill_engine.store import SkillStore + from scion.skill_engine.tag_search import TagSearch store = SkillStore(db_path=tmp_path / "facade_test.db") @@ -698,7 +698,7 @@ def test_skill_store_facade_completeness(self, tmp_path: Path): def test_facade_method_delegation(self, tmp_path: Path): """Verify facade methods actually delegate to TagSearch.""" - from openspace.skill_engine.store import SkillStore + from scion.skill_engine.store import SkillStore store = SkillStore(db_path=tmp_path / "delegation_test.db") @@ -736,7 +736,7 @@ class TestBackwardCompatibility: def test_skill_store_get_stats_field_names(self, tmp_path: Path): """Verify get_stats returns backward-compatible field names.""" - from openspace.skill_engine.store import SkillStore + from scion.skill_engine.store import SkillStore store = SkillStore(db_path=tmp_path / "compat_test.db") @@ -759,7 +759,7 @@ def test_skill_store_get_stats_field_names(self, tmp_path: Path): def test_tag_search_direct_call_uses_original_field(self, tmp_path: Path): """Verify TagSearch.get_stats() returns the corrected field name.""" - from openspace.skill_engine.tag_search import TagSearch + from scion.skill_engine.tag_search import TagSearch tag_search = TagSearch(db_path=tmp_path / "direct_test.db") diff --git a/tests/test_traceback_safety.py b/tests/test_traceback_safety.py index cae47a9..e2a8bb9 100644 --- a/tests/test_traceback_safety.py +++ b/tests/test_traceback_safety.py @@ -60,20 +60,20 @@ def _assert_structured_error(response_str: str, tool_name: str) -> dict: return data -# ── Unit tests for openspace.errors ────────────────────────────────── +# ── Unit tests for scion.errors ────────────────────────────────── class TestSanitizeError: """Test that sanitize_error strips dangerous content.""" def test_plain_message_preserved(self): - from openspace.errors import sanitize_error + from scion.errors import sanitize_error exc = ValueError("missing required field") assert sanitize_error(exc) == "missing required field" def test_traceback_string_stripped(self): - from openspace.errors import sanitize_error + from scion.errors import sanitize_error exc = RuntimeError('Traceback (most recent call last):\n File "foo.py", line 42\nKeyError') result = sanitize_error(exc) @@ -81,7 +81,7 @@ def test_traceback_string_stripped(self): assert "foo.py" not in result def test_file_path_stripped(self): - from openspace.errors import sanitize_error + from scion.errors import sanitize_error exc = OSError("Cannot open C:\\Users\\dev\\project\\secret.py") result = sanitize_error(exc) @@ -89,7 +89,7 @@ def test_file_path_stripped(self): assert "secret.py" not in result def test_unix_path_stripped(self): - from openspace.errors import sanitize_error + from scion.errors import sanitize_error exc = OSError("Failed at /home/user/openspace/mcp_server.py") result = sanitize_error(exc) @@ -97,7 +97,7 @@ def test_unix_path_stripped(self): assert "mcp_server.py" not in result def test_empty_message_returns_generic(self): - from openspace.errors import sanitize_error + from scion.errors import sanitize_error exc = RuntimeError() result = sanitize_error(exc) @@ -105,7 +105,7 @@ def test_empty_message_returns_generic(self): assert "RuntimeError" not in result def test_long_message_truncated(self): - from openspace.errors import sanitize_error + from scion.errors import sanitize_error exc = ValueError("x" * 500) result = sanitize_error(exc) @@ -114,7 +114,7 @@ def test_long_message_truncated(self): # ── Regression tests from /collab + /8eyes review ──────────── def test_windows_path_with_spaces_stripped(self): - from openspace.errors import sanitize_error + from scion.errors import sanitize_error exc = OSError(r"Cannot open C:\Program Files\OpenSpace\secret.py") result = sanitize_error(exc) @@ -123,7 +123,7 @@ def test_windows_path_with_spaces_stripped(self): assert "secret.py" not in result def test_unc_path_stripped(self): - from openspace.errors import sanitize_error + from scion.errors import sanitize_error exc = OSError(r"Failed reading \\server\share\secret.py") result = sanitize_error(exc) @@ -131,15 +131,15 @@ def test_unc_path_stripped(self): assert "secret.py" not in result def test_dotted_module_name_stripped(self): - from openspace.errors import sanitize_error + from scion.errors import sanitize_error - exc = ImportError("No module named openspace.cloud.auth.TokenResolver") + exc = ImportError("No module named scion.cloud.auth.TokenResolver") result = sanitize_error(exc) - assert "openspace.cloud.auth" not in result + assert "scion.cloud.auth" not in result assert "TokenResolver" not in result def test_standalone_line_number_stripped(self): - from openspace.errors import sanitize_error + from scion.errors import sanitize_error exc = RuntimeError("failed at line 99 in processing") result = sanitize_error(exc) @@ -147,7 +147,7 @@ def test_standalone_line_number_stripped(self): def test_never_returns_exception_class_name(self): """Regression: fallback must not leak type(exc).__name__.""" - from openspace.errors import sanitize_error + from scion.errors import sanitize_error class InternalSecretError(Exception): pass @@ -162,7 +162,7 @@ class TestSafeErrorResponse: """Test the structured JSON builder.""" def test_schema(self): - from openspace.errors import EXECUTION_ERROR, safe_error_response + from scion.errors import EXECUTION_ERROR, safe_error_response raw = safe_error_response(EXECUTION_ERROR, "Something went wrong") data = json.loads(raw) @@ -173,7 +173,7 @@ def test_schema(self): assert len(data["correlation_id"]) == 12 def test_custom_correlation_id(self): - from openspace.errors import VALIDATION_ERROR, safe_error_response + from scion.errors import VALIDATION_ERROR, safe_error_response raw = safe_error_response(VALIDATION_ERROR, "bad input", correlation_id="abc123") data = json.loads(raw) @@ -184,9 +184,9 @@ class TestHandleMcpException: """Test the one-liner exception handler.""" def test_logs_and_returns_safe_json(self): - from openspace.errors import EXECUTION_ERROR, handle_mcp_exception + from scion.errors import EXECUTION_ERROR, handle_mcp_exception - with patch("openspace.errors.logger") as mock_logger: + with patch("scion.errors.logger") as mock_logger: exc = RuntimeError("boom at /secret/path.py:42") result = handle_mcp_exception(exc, tool_name="test_tool", error_code=EXECUTION_ERROR) @@ -200,9 +200,9 @@ def test_logs_and_returns_safe_json(self): _assert_no_traceback_leak(result, "test_tool") def test_correlation_id_in_log_and_response(self): - from openspace.errors import INTERNAL_ERROR, handle_mcp_exception + from scion.errors import INTERNAL_ERROR, handle_mcp_exception - with patch("openspace.errors.logger") as mock_logger: + with patch("scion.errors.logger") as mock_logger: exc = ValueError("oops") result = handle_mcp_exception(exc, tool_name="x", error_code=INTERNAL_ERROR) @@ -228,7 +228,7 @@ def _make_openspace_mock(): @pytest.fixture(autouse=True) def _patch_openspace_init(monkeypatch): """Prevent real OpenSpace initialization in every test.""" - import openspace.mcp_server as srv + import scion.mcp_server as srv mock = _make_openspace_mock() monkeypatch.setattr(srv, "_openspace_instance", mock) @@ -240,7 +240,7 @@ def _patch_openspace_init(monkeypatch): @pytest.mark.asyncio async def test_execute_task_error_no_traceback(): """execute_task: exception → structured error, no traceback leak.""" - import openspace.mcp_server as srv + import scion.mcp_server as srv with patch.object( srv, @@ -258,7 +258,7 @@ async def test_execute_task_error_no_traceback(): @pytest.mark.asyncio async def test_execute_task_deep_traceback_not_leaked(): """execute_task: real traceback chain → nothing leaks.""" - import openspace.mcp_server as srv + import scion.mcp_server as srv def _blow_up(): raise KeyError("secret_key") @@ -282,15 +282,15 @@ async def _explode(): @pytest.mark.asyncio async def test_search_skills_error_no_traceback(): """search_skills: exception → structured error, no traceback leak.""" - import openspace.mcp_server as srv + import scion.mcp_server as srv with patch( - "openspace.mcp_server.hybrid_search_skills", + "scion.mcp_server.hybrid_search_skills", create=True, - side_effect=ImportError("No module named 'openspace.cloud.search'"), + side_effect=ImportError("No module named 'scion.cloud.search'"), ): # The import happens inside the try block, so we need to make it raise - with patch.dict("sys.modules", {"openspace.cloud.search": None}): + with patch.dict("sys.modules", {"scion.cloud.search": None}): result = await srv.search_skills(query="test query") _assert_no_traceback_leak(result, "search_skills") @@ -303,7 +303,7 @@ async def test_search_skills_error_no_traceback(): @pytest.mark.asyncio async def test_fix_skill_missing_skill_md(): """fix_skill: missing SKILL.md → SKILL_NOT_FOUND, no path leak.""" - import openspace.mcp_server as srv + import scion.mcp_server as srv result = await srv.fix_skill( skill_dir="/secret/internal/path/my-skill", @@ -323,7 +323,7 @@ async def test_fix_skill_exception_no_traceback(): """fix_skill: runtime exception → structured error.""" import os - import openspace.mcp_server as srv + import scion.mcp_server as srv # Create a temporary directory with SKILL.md so we pass validation tmpdir = os.path.join(os.path.dirname(__file__), "_test_skill_tmp") @@ -356,7 +356,7 @@ async def test_fix_skill_exception_no_traceback(): @pytest.mark.asyncio async def test_upload_skill_missing_skill_md(): """upload_skill: missing SKILL.md → SKILL_NOT_FOUND, no path leak.""" - import openspace.mcp_server as srv + import scion.mcp_server as srv result = await srv.upload_skill(skill_dir="/opt/secret/skills/broken") @@ -372,7 +372,7 @@ async def test_upload_skill_exception_no_traceback(): """upload_skill: cloud auth failure → structured error, no traceback.""" import os - import openspace.mcp_server as srv + import scion.mcp_server as srv tmpdir = os.path.join(os.path.dirname(__file__), "_test_upload_tmp") os.makedirs(tmpdir, exist_ok=True) @@ -403,9 +403,9 @@ async def test_upload_skill_exception_no_traceback(): @pytest.mark.asyncio async def test_server_logs_full_exception(): """Verify that full exception details are logged server-side.""" - import openspace.mcp_server as srv + import scion.mcp_server as srv - with patch("openspace.errors.logger") as mock_logger: + with patch("scion.errors.logger") as mock_logger: with patch.object( srv, "_get_openspace", @@ -439,7 +439,7 @@ async def test_server_logs_full_exception(): ) async def test_all_tools_structured_error_on_failure(tool_name, call): """Every MCP tool returns structured error JSON on failure — never raw tracebacks.""" - import openspace.mcp_server as srv + import scion.mcp_server as srv # Sabotage everything to force errors with patch.object( @@ -448,7 +448,7 @@ async def test_all_tools_structured_error_on_failure(tool_name, call): new_callable=AsyncMock, side_effect=Exception(f"Synthetic failure in {tool_name}"), ): - with patch.dict("sys.modules", {"openspace.cloud.search": None}): + with patch.dict("sys.modules", {"scion.cloud.search": None}): result = await call(srv) _assert_no_traceback_leak(result, tool_name)