anthropics / anthropics/anthropic-sdk-python

Managed Agents self-hosted worker: download_session_skills skips roster agents' skills, so subagent threads run without them

Aberta
#1,870 1 comentário 0 reações 0 responsáveis Ver no GitHub
Linguagem predominante
Python
Estrelas
3.9k
Forks
853
Merge médio
1d 18h
PRs com merge (30d)
11

Descrição

### Summary

`download_session_skills` (`src/anthropic/lib/tools/_skills.py`) only iterates `session.agent.skills`. In a **multiagent** session, the roster members' skills live at `session.agent.multiagent.agents[*].skills` and are never downloaded.

Subagent threads share the container filesystem with the coordinator, so on a **self-hosted** environment (`client.beta.environments.work.worker(...)` / `EnvironmentWorker.handle_item()`) a subagent runs with its skills missing from `{workdir}/skills/`. This fails **silently**: no exception, no warning — the thread simply behaves as if the skill were not attached, and the only symptom is degraded output.

Cloud sandboxes are unaffected (Anthropic mounts skills there), which is presumably why this hasn't surfaced.

### Version

`anthropic==1.0.0`, Python 3.12. Also present in `0.105.2`, so this is not a v1 regression. `grep -r multiagent src/anthropic/lib/` returns no hits at all — the worker / toolset / skills layers have no notion of the roster. I searched open and closed issues (`skills`, `subagent`, `multiagent`, `self-hosted skills`) and found no existing report; the closest is #1562 (vault_ids not inherited by subagents), which is the same class of coordinator-config-not-propagating-to-subagents gap.

### Repro

1. Create a worker agent with a skill attached, e.g. `skills=[{"type": "custom", "skill_id": "skill_abc123"}]`, and **no** skills on the coordinator.
2. Create a coordinator agent with `multiagent={"type": "coordinator", "agents": [worker_agent.id]}`.
3. Create a `self_hosted` environment and run `EnvironmentWorker(client, workdir="/workspace", ...)`.
4. Start a session on the coordinator and have it delegate to the worker agent.
5. `{workdir}/skills/` is empty (only the coordinator's skills appear when it has any). The subagent thread runs without its skill; nothing is logged.

### Root cause

`_skills.py:261`:

```python
for skill in session.agent.skills:
```

This is the only loop; `AgentToolContext.setup_skills` (`agent_toolset.py:309`) is its only caller and passes the session through unchanged.

### Why this is straightforward to fix

The roster is already fully resolved on the object the function receives — no extra API call is needed:

- `session.agent` is `BetaManagedAgentsSessionAgent`, whose `multiagent` is `BetaManagedAgentsSessionMultiagentCoordinator` — "Resolved coordinator topology with **full agent definitions** for each roster member".
- Each entry is `BetaManagedAgentsSessionThreadAgent | BetaManagedAgentsAdvisor`; the former carries `skills: List[Skill]`, the latter carries only `model` / `type`.

(Note this holds specifically for the *session* shape: `BetaManagedAgentsAgent.multiagent` carries `BetaManagedAgentsAgentReference` entries with no skills, so the fix must read the roster off the session, as the function already does for `session.agent`.)

### Suggested fix

Collect skills from the coordinator plus every roster entry, skipping advisor entries, and dedupe by `skill_id` so a skill shared by two agents isn't downloaded (and `rmtree`'d) twice:

```python
def _session_skills(session: BetaManagedAgentsSession) -> list[Skill]:
skills = list(session.agent.skills)
multiagent = session.agent.multiagent
if multiagent is not None:
for entry in multiagent.agents:
skills.extend(getattr(entry, "skills", ())) # advisor entries have none
seen: set[str] = set()
return [s for s in skills if not (s.skill_id in seen or seen.add(s.skill_id))]
```

and iterate that instead of `session.agent.skills`. Happy to open a PR if useful.

I've only verified the Python SDK; the TypeScript worker may have the same shape.

Guia de contribuição

Abrir o guia de contribuição

Avaliação

Esta issue ainda não foi avaliada.

Receba novas issues na sua caixa de entrada

Um resumo curto de issues do GitHub para quem está começando.