OpenHands / OpenHands/software-agent-sdk
[Bug]: Process-global subagent registry leaks definitions across conversations
Nobody has claimed this yet.
- Dominant language
- Python
- Stars
- 1.1k
- Forks
- 539
- Avg merge
- 1d 19h
- Merged PRs (30d)
- 137
Description
Bug Description
File/plugin subagent definitions are conversation-specific, but every factory is stored in one process-global first-writer-wins registry (openhands-sdk/openhands/sdk/subagent/registry.py:56-58,124-152,288-367).
Each LocalConversation registers its plugin and workspace definitions into that registry (conversation/impl/local_conversation.py:1211-1216,1482-1499). Two conversations can therefore define the same subagent name differently; whichever initializes first silently controls both. Remote creation also serializes all definitions visible in the process (remote_conversation.py:807-833), which can include definitions from unrelated workspaces. Agent Server repeats the same global registration for per-conversation payloads (conversation_service.py:513-545).
Expected Behavior
Built-in/programmatic definitions may have process ownership, but plugin/project/user file definitions must resolve in the conversation that discovered them. Same-name definitions in different conversations must not collide or leak.
Actual Behavior
An independent, self-contained reproducer is posted in the issue comments. Save
it as /tmp/repro_4661.py, then run:
uv run python /tmp/repro_4661.py
On SDK commit 6d3881035, it exercises two real project trees and the actual file-agent registry; the test-only reset is used solely to run both initialization orders independently and prints:
first_project=project-a
first_registered=['shared-repro-agent']
second_registered=[]
global_winner=definition-from-A
remote_forwarded_definition=['definition-from-A']
first_project=project-b
first_registered=['shared-repro-agent']
second_registered=[]
global_winner=definition-from-B
remote_forwarded_definition=['definition-from-B']
Suggested Implementation
Retain a process-level base registry for explicit programmatic/built-in registrations and add a conversation-scoped overlay/resolver for plugin/project/user definitions. Task/delegate lookup and remote serialization should consume that resolver.
Acceptance Criteria
- Two live local conversations can use same-name, different project definitions in either initialization order.
- Two server conversations can do the same concurrently.
- Remote creation serializes only definitions available to that conversation.
- Existing precedence remains: programmatic > plugin > project > user > built-in.
- Persisted/remote resume and task/delegate lookup remain compatible.
Version
Current main at 6d3881035. Found during the independent coverage pass of a read-only ownership audit.
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start with the reproducer at /tmp/repro_4661.py, then trace registry.py, local_conversation.py, remote_conversation.py, and conversation_service.py at the cited ranges. Follow how definitions are registered, resolved, serialized, resumed, and used for task/delegate lookup. Done means the listed acceptance checks pass for local, server, remote, precedence, and resume behavior.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- python
- Domain
- api, backend
- Issue type
- Bug
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Active
- Clarity
- Clearly specified
- Newbie friendliness
- 35/100