anthropics / anthropics/anthropic-sdk-python

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

未關閉
#1,870 1 則留言 0 個 reaction 已指派 0 人 在 GitHub 檢視
主要語言
Python
星號
3.9k
分支
853
平均合併
1 天 18 小時
30 天內合併 PR
11

描述

### 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.

貢獻指南

開啟貢獻指南

評估

這個 Issue 還沒有評估資料。

把新 issue 寄到你的電子郵件信箱

精選適合新手參與的 GitHub issue 摘要。