Skip to content

Swift SDK RelayCast is incompatible with v7 broker (hello/hello_ack handshake removed) #988

@willwashburn

Description

@willwashburn

Summary

The Swift SDK (packages/sdk-swift, AgentRelaySDK.RelayCast) cannot connect to a v7.1.1 broker even when both are at matching version (7.1.1). RelayCast is still implementing the legacy hello/hello_ack WebSocket handshake, but the v7 broker's /ws endpoint is now a read-only event broadcast (handle_dashboard_ws in crates/broker/src/listen_api.rs) — it never sends hello_ack, so every Swift SDK connection times out.

Repro

  1. npm install -g agent-relay@7.1.1
  2. From a fresh CWD, start a broker: agent-relay up --no-dashboard --foreground --state-dir ./.agent-relay
  3. Build a minimal Swift consumer of AgentRelaySDK:
import AgentRelaySDK
let conn = try! JSONDecoder().decode(
    [String: String].self,
    from: try! Data(contentsOf: URL(fileURLWithPath: ".agent-relay/connection.json"))
)
let relay = RelayCast(apiKey: conn["api_key"]!, baseURL: URL(string: conn["url"]!)!)
try await relay.channel("test").subscribe()   // throws timeout

Observed:

RelayError.connectionFailed("NSURLErrorDomain Code=-1011 \"There was a bad response from the server.\" ...")

…or, if /ws resolves correctly:

RelayError.timeout("Timed out waiting for hello_ack")

Expected: Connection succeeds and events stream.

Root cause (confirmed by reading sources)

1. Wrong WebSocket path

Sources/AgentRelaySDK/RelayTransport.swift:101-107 defaults the WS path to /v1/ws:

if components?.path.isEmpty ?? true { components?.path = \"/v1/ws\" }
if !(components?.path.hasSuffix(\"/v1/ws\") ?? false) && !(components?.path.hasSuffix(\"/ws\") ?? false) {
    components?.path = \"/v1/ws\"
}

But the v7 broker registers /ws (no /v1 prefix) — see crates/broker/src/listen_api.rs:414:

.route(\"/ws\", routing::get(listen_api_ws))

A GET on /v1/ws with valid auth returns 404; only /ws is live (returns 400 to a non-upgrade GET).

2. Wrong protocol on /ws

Even after correcting the path, the Swift SDK sends a hello frame (RelayCast.swift:88) and awaits hello_ack. But v7's /ws handler is handle_dashboard_ws (crates/broker/src/listen_api.rs:1994-) — it's a read-only broadcast stream. It never reads from the socket beyond ping/pong and never sends hello_ack. Result: Timed out waiting for hello_ack.

For comparison, the official Node SDK (@agent-relay/sdk v7.1.1) opens the same /ws with header X-API-Key, does not send a hello, and just receives events (packages/sdk/dist/transport.js:375-417). Actions (spawn_agent, send_message, release_agent) are now plain HTTP — POST /api/spawn, POST /api/send, DELETE /api/spawned/{name} etc. (crates/broker/src/listen_api.rs:357-412).

3. The newer Swift RelayObserver is v7-compatible but read-only

packages/sdk-swift/Sources/AgentRelaySDK/RelayObserver.swift (added in #627) implements the v7 event-stream contract correctly. But it only consumes events — there is no Swift counterpart for the HTTP action endpoints, so an SDK consumer can't actually drive agents.

Suggested fix

Realign RelayCast with the v7 contract:

  1. Drop the hello/hello_ack handshake. Connect to /ws with X-API-Key, treat "connected" as "WS upgrade succeeded."
  2. Use /ws, not /v1/ws. Or detect at connect time (probe with auth: 400→/ws, 404→try other).
  3. Move SDK actions (spawnAgent, releaseAgent, channel post, agent dm) onto HTTP. Map to the existing broker REST endpoints:
    • spawnAgentPOST /api/spawn
    • releaseAgentDELETE /api/spawned/{name}
    • Channel postPOST /api/send
  4. Keep RelayCast.channel(...).events and brokerEvents as views over the WS event stream (same shape as today) — wire it through RelayObserver-style decoding.

This restores byte-compat behavior for consumers (spawnAndAwait etc. still work) while moving the wire format to v7.

Impact

Any Swift consumer of RelayCast against a v7 broker — including locally-spawned brokers via agent-relay up. Caught while building Axiom, a SwiftUI macOS planning-conversation app that needs to drive a local Claude agent over Relay. Blocked end-to-end until RelayCast either gets the v7 fix or we route around it via raw HTTP + RelayObserver.

Environment

  • macOS 15 (Darwin 25.5.0), Apple Silicon
  • Swift toolchain 5.9+, Package.swift pins relay to ff1d4da (v7.1.1)
  • Broker: agent-relay@7.1.1 (npm), broker binary @agent-relay/broker-darwin-arm64

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions