Summary
Self-hosted workers that serve multiple sessions concurrently from one workdir (max_concurrent_sessions > 1) hit two problems:
download_session_skills unconditionally rmtrees and re-extracts each skill directory even when the resolved version_id is unchanged, so every new session re-downloads the same archives.
AgentToolContext._cleanup_skills removes those directories when any one session ends.
Together these mean a session in flight can have its skill files deleted or replaced underneath it by another session starting or finishing. The window is small but real, and the redundant downloads are pure overhead on a long-running worker.
Version
anthropic==1.0.0, Python 3.12.
Repro
- Run one
EnvironmentWorker process serving two sessions concurrently with the same workdir (both agents attach the same skill).
- Session B starts while session A is running: the skill directory A is using is
rmtree'd and re-extracted.
- Session A ends while B is running:
_cleanup_skills removes the directory B is using.
Workaround in use
We monkey-patch AgentToolContext.setup_skills with a process-wide cache (a skill_id -> version_id map behind an asyncio.Lock, downloading only when the resolved version differs) and neutralise the teardown by leaving _skill_dirs empty. This works but pins us to internals (_skills._resolve_skill_version, _skills._download_and_extract).
Request
Either of these would let us drop the patch:
- Skip the download when the resolved
version_id matches what is already on disk (an idempotent setup_skills), and
- an opt-out for the teardown
rmtree — e.g. AgentToolContext(keep_skills=True) — so a shared skills tree can be treated as a process-wide cache owned by the worker rather than by an individual session.
Happy to open a PR if you have a preference on the shape.
Summary
Self-hosted workers that serve multiple sessions concurrently from one
workdir(max_concurrent_sessions > 1) hit two problems:download_session_skillsunconditionallyrmtrees and re-extracts each skill directory even when the resolvedversion_idis unchanged, so every new session re-downloads the same archives.AgentToolContext._cleanup_skillsremoves those directories when any one session ends.Together these mean a session in flight can have its skill files deleted or replaced underneath it by another session starting or finishing. The window is small but real, and the redundant downloads are pure overhead on a long-running worker.
Version
anthropic==1.0.0, Python 3.12.Repro
EnvironmentWorkerprocess serving two sessions concurrently with the sameworkdir(both agents attach the same skill).rmtree'd and re-extracted._cleanup_skillsremoves the directory B is using.Workaround in use
We monkey-patch
AgentToolContext.setup_skillswith a process-wide cache (askill_id -> version_idmap behind anasyncio.Lock, downloading only when the resolved version differs) and neutralise the teardown by leaving_skill_dirsempty. This works but pins us to internals (_skills._resolve_skill_version,_skills._download_and_extract).Request
Either of these would let us drop the patch:
version_idmatches what is already on disk (an idempotentsetup_skills), andrmtree— e.g.AgentToolContext(keep_skills=True)— so a shared skills tree can be treated as a process-wide cache owned by the worker rather than by an individual session.Happy to open a PR if you have a preference on the shape.