Skip to content

Commit 2cf2c09

Browse files
committed
docs(skills): clarify LocalDirLazySkillSource host paths
1 parent e80d2d2 commit 2cf2c09

4 files changed

Lines changed: 25 additions & 3 deletions

File tree

docs/sandbox/guide.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -205,6 +205,7 @@ By default, `SandboxAgent.capabilities` uses `Capabilities.default()`, which inc
205205
For skills, choose the source based on how you want them materialized:
206206

207207
- `Skills(lazy_from=LocalDirLazySkillSource(...))` is a good default for larger local skill directories because the model can discover the index first and load only what it needs.
208+
- `LocalDirLazySkillSource(source=LocalDir(...))` reads from the host filesystem, not from a path that already exists inside the sandbox image or workspace. If you mount skills into the sandbox at `/skills`, still pass the original host directory to `LocalDir(...)`.
208209
- `Skills(from_=LocalDir(src=...))` is better for a small local bundle you want staged up front.
209210
- `Skills(from_=GitRepo(repo=..., ref=...))` is the right fit when the skills themselves should come from a repository.
210211

docs/sandbox_agents.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,8 @@ def build_agent(model: str) -> SandboxAgent[None]:
6565
capabilities=Capabilities.default() + [
6666
Skills(
6767
lazy_from=LocalDirLazySkillSource(
68+
# This is a host path. The lazy loader copies skills into `.agents/`
69+
# on demand, so do not point this at an in-sandbox path such as `/skills`.
6870
source=LocalDir(src=HOST_SKILLS_DIR),
6971
)
7072
),

src/agents/sandbox/capabilities/skills.py

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -173,8 +173,17 @@ async def load_skill(
173173
src_root = self._src_root()
174174
if src_root is None:
175175
raise SkillsConfigError(
176-
message="lazy skill source directory is unavailable",
177-
context={"skill_name": skill_name},
176+
message=(
177+
"lazy skill source directory is unavailable; "
178+
"LocalDirLazySkillSource expects a host filesystem path, not a path "
179+
"inside the sandbox"
180+
),
181+
context={
182+
"skill_name": skill_name,
183+
"configured_source_path": None
184+
if self.source.src is None
185+
else str(self.source.src),
186+
},
178187
)
179188

180189
matches = [

tests/sandbox/capabilities/test_skills_capability.py

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -556,9 +556,19 @@ async def test_load_skill_rejects_missing_lazy_source_directory(self, tmp_path:
556556
)
557557
capability.bind(_SkillsSession(Manifest(root=str(workspace_root))))
558558

559-
with pytest.raises(SkillsConfigError):
559+
with pytest.raises(SkillsConfigError) as exc_info:
560560
await capability.load_skill("missing-skill")
561561

562+
assert (
563+
exc_info.value.message
564+
== "lazy skill source directory is unavailable; "
565+
"LocalDirLazySkillSource expects a host filesystem path, not a path inside the sandbox"
566+
)
567+
assert exc_info.value.context == {
568+
"skill_name": "missing-skill",
569+
"configured_source_path": str(tmp_path / "missing-skills"),
570+
}
571+
562572
@pytest.mark.asyncio
563573
async def test_load_skill_rejects_ambiguous_skill_name(self, tmp_path: Path) -> None:
564574
workspace_root = tmp_path / "workspace"

0 commit comments

Comments
 (0)