diff --git a/astrbot/core/skills/skill_manager.py b/astrbot/core/skills/skill_manager.py index 1b71e5371f..660dafbc6b 100644 --- a/astrbot/core/skills/skill_manager.py +++ b/astrbot/core/skills/skill_manager.py @@ -24,7 +24,6 @@ SANDBOX_SKILLS_CACHE_FILENAME = "sandbox_skills_cache.json" DEFAULT_SKILLS_CONFIG: dict[str, dict] = {"skills": {}} SANDBOX_SKILLS_ROOT = "skills" -SANDBOX_WORKSPACE_ROOT = "/workspace" _SANDBOX_SKILLS_CACHE_VERSION = 1 _SKILL_NAME_RE = re.compile(r"^[\w.-]+$") @@ -36,7 +35,7 @@ def _normalize_skill_name(name: str | None) -> str: def _default_sandbox_skill_path(name: str) -> str: - return f"{SANDBOX_WORKSPACE_ROOT}/{SANDBOX_SKILLS_ROOT}/{name}/SKILL.md" + return f"{SANDBOX_SKILLS_ROOT}/{name}/SKILL.md" def _normalize_cached_sandbox_skill_path(name: str, path: str) -> str: @@ -253,8 +252,8 @@ def build_skills_prompt(skills: list[SkillInfo]) -> str: "explain why you chose not to.\n" "3. **Mandatory grounding** — Before executing any skill you MUST " "first read its `SKILL.md` by running a shell command compatible " - "with the current runtime shell and using the **absolute path** " - f"shown above (e.g. `{example_command}`). " + "with the current runtime shell and using the path shown above " + f"(e.g. `{example_command}`). " "Never rely on memory or assumptions about a skill's content.\n" "4. **Progressive disclosure** — Load only what is directly " "referenced from `SKILL.md`:\n" diff --git a/tests/test_skill_manager_sandbox_cache.py b/tests/test_skill_manager_sandbox_cache.py index 35fb608118..69c1ac7de1 100644 --- a/tests/test_skill_manager_sandbox_cache.py +++ b/tests/test_skill_manager_sandbox_cache.py @@ -155,3 +155,40 @@ def test_sandbox_and_local_path_resolution_with_show_sandbox_path_false( assert local_skill_path == skills_root / "custom-local" / "SKILL.md" assert by_name["python-sandbox"].path == "/app/skills/python-sandbox/SKILL.md" + +def test_sandbox_path_fallback_uses_relative_skills_root( + monkeypatch, + tmp_path: Path, +): + data_dir = tmp_path / "data" + temp_dir = tmp_path / "temp" + skills_root = tmp_path / "skills" + data_dir.mkdir(parents=True, exist_ok=True) + temp_dir.mkdir(parents=True, exist_ok=True) + skills_root.mkdir(parents=True, exist_ok=True) + + monkeypatch.setattr( + "astrbot.core.skills.skill_manager.get_astrbot_data_path", + lambda: str(data_dir), + ) + monkeypatch.setattr( + "astrbot.core.skills.skill_manager.get_astrbot_temp_path", + lambda: str(temp_dir), + ) + + mgr = SkillManager(skills_root=str(skills_root)) + mgr.set_sandbox_skills_cache( + [ + { + "name": "docx", + "description": "sandbox skill", + "path": "/workspace/not-the-real-root/docx/readme.txt", + } + ] + ) + + skills = mgr.list_skills(runtime="sandbox") + + assert len(skills) == 1 + assert skills[0].path == "skills/docx/SKILL.md" + diff --git a/tests/test_skill_metadata_enrichment.py b/tests/test_skill_metadata_enrichment.py index 78d84104f9..d68f9610db 100644 --- a/tests/test_skill_metadata_enrichment.py +++ b/tests/test_skill_metadata_enrichment.py @@ -128,6 +128,23 @@ def test_build_skills_prompt_keeps_placeholder_example_literal(): assert example_fragment == "cat //SKILL.md" +def test_build_skills_prompt_allows_relative_sandbox_path_examples(): + skills = [ + SkillInfo( + name="docx", + description="sandbox skill", + path="skills/docx/SKILL.md", + active=True, + source_type="sandbox_only", + ) + ] + + prompt = build_skills_prompt(skills) + + assert "cat skills/docx/SKILL.md" in prompt + assert "**absolute path**" not in prompt + + def test_build_skills_prompt_preserves_windows_absolute_path_in_example(monkeypatch): monkeypatch.setattr("astrbot.core.skills.skill_manager.os.name", "nt") skills = [