OpenHands / OpenHands/software-agent-sdk

[Feature]: Injectable WebSocket client factory for RemoteConversation

Open
#4,708 3 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

enhancement ready-for-dev
Dominant language
Python
Stars
1.1k
Forks
539
Avg merge
1d 19h
Merged PRs (30d)
137

Description

Is there an existing feature request for this?
  • I have searched existing issues and feature requests, and this is not a duplicate.
Problem or Use Case

RemoteConversation constructs WebSocketCallbackClient directly at a single
point in __init__. There is no supported way for a caller to supply a
transport-specific callback client.

The concrete use case is running the controller with --network none, talking to
an agent server over a Unix-domain socket via a relay, so a WebSocket-over-UDS
client is needed in place of the default TCP one.

Today the only way to achieve this is to replace the module global
openhands.sdk.conversation.impl.remote_conversation.WebSocketCallbackClient,
which is process-wide. That prevents two conversations in the same process from
using different transports, and it depends on a private module attribute that is
not part of the public API.

Desired Behavior

Add an optional, keyword-only websocket_client_factory argument to
RemoteConversation, plus a typed WebSocketClientFactory protocol describing
the construction contract (host, conversation_id, callback, api_key,
on_reconnect).

When the argument is omitted, behavior is unchanged: the default
WebSocketCallbackClient is used. The existing client keeps ownership of
startup, readiness waiting, initial reconciliation, failure cleanup, and
shutdown — the factory only decides which client is constructed.

A Protocol is preferred over a bare Callable[..., Any] so an incompatible
factory is rejected at type-check time rather than at WebSocket-connect time.

The default must stay late-bound (resolved when __init__ runs, not captured as
a default argument value), because the existing test suite patches the module
attribute in 87 patch() sites across 10 test files.

Acceptance Criteria
  • RemoteConversation accepts an optional websocket_client_factory keyword argument
  • A WebSocketClientFactory protocol types the contract with host, conversation_id, callback, api_key, and on_reconnect
  • An injected factory is used instead of the default, and receives all five keyword arguments
  • Omitting the argument still constructs WebSocketCallbackClient, resolved at call time so existing module-level patching keeps working
  • WebSocketCallbackClient satisfies the protocol's keyword contract
  • uv run pytest tests/sdk/conversation passes with no new failures
Alternatives Considered

Replacing the module global WebSocketCallbackClient at runtime. This works, but
it is process-wide, so conversations cannot use independent transports, and it
relies on a private attribute with no compatibility guarantee.

Subclassing RemoteConversation to override client construction. The
construction is inline in __init__ rather than in an overridable method, so
this would mean copying the whole __init__ body.

Priority / Severity

Medium - Would improve experience

Estimated Scope

Small - API addition, config option, or minor change

Feature Area
  • Agent API / Core functionality
  • Tools / Tool system
  • Skills / Plugins
  • Agent Server
  • Workspace management
  • Configuration / Settings
  • Examples / Templates
  • Documentation
  • Testing / Development tools
  • Performance / Optimization
  • Integrations (GitHub, APIs, etc.)
  • Other
Technical Implementation Ideas (Optional)

No response

Additional Context

No response

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start in openhands.sdk.conversation.impl.remote_conversation, focusing on RemoteConversation.init and the existing WebSocketCallbackClient construction. Read the tests under tests/sdk/conversation, then run uv run pytest tests/sdk/conversation while checking the acceptance criteria: the injected factory receives all five keyword arguments, and the default remains late-bound and compatible with existing patches.

Written by the indexing model from the issue text.

Assessment

Tech stack
python
Domain
api, backend
Issue type
Feature
Difficulty
3/5
Estimated time
1-2 days
Activity status
Active
Clarity
Clearly specified
Newbie friendliness
72/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.