fix(#1197): advertise server request capability (#1198)

This commit is contained in:
M57 authored and GitHub committed 2026-09-19 20:08:11 +03:00
1 parent 759d59b9f9
commit c96a5ff4da
3 files changed
+54

No files matched your search

@@ -354,6 +354,14 @@ object HermesWsClient {
wsScope.launch {
events.collect { event ->
if (event is WsEvent.GatewayReady) {
// Advertise support for gateway server→client requests (issue #1197).
// Older gateways may reject this method; that is harmless.
runCatching {
send(
WsMethods.CLIENT_CAPABILITIES,
mapOf("server_requests" to true),
)
}
val epoch = event.data?.get("replay_epoch") as? String
if (!epoch.isNullOrEmpty()) {
if (replayEpoch != null && replayEpoch != epoch) {
@@ -66,6 +66,7 @@ object WsMethods {
/** Inline WebSocket read-loop ping, bypasses server thread pool dispatch. */
const val GATEWAY_PING = "gateway.ping"
const val CLIENT_CAPABILITIES = "client.capabilities"
// ── Commands catalog ──────────────────────────────────────────────────
const val COMMANDS_CATALOG = "commands.catalog"
@@ -176,6 +176,51 @@ class HermesWsClientTest {
assertTrue(msg.contains(id))
}
@Test
fun gatewayReadyAdvertisesServerRequestCapability() {
val capabilityLatch = CountDownLatch(1)
var capabilityFrame: String? = null
mockWebServer.enqueue(
MockResponse().withWebSocketUpgrade(
object : WebSocketListener() {
override fun onOpen(
webSocket: WebSocket,
response: okhttp3.Response,
) {
webSocket.send(
"""{"jsonrpc":"2.0","method":"event","params":{"type":"gateway.ready","payload":{}}}""",
)
}
override fun onMessage(
webSocket: WebSocket,
text: String,
) {
val frame = Json.parseToJsonElement(text).jsonObject
if (frame["method"]?.jsonPrimitive?.content == WsMethods.CLIENT_CAPABILITIES) {
capabilityFrame = text
capabilityLatch.countDown()
}
}
},
),
)
HermesWsClient.connect()
assertTrue("Capability advertisement not sent", capabilityLatch.await(5, TimeUnit.SECONDS))
val frame = Json.parseToJsonElement(capabilityFrame ?: "{}").jsonObject
assertEquals(WsMethods.CLIENT_CAPABILITIES, frame["method"]?.jsonPrimitive?.content)
assertTrue(
frame["params"]
?.jsonObject
?.get("server_requests")
?.jsonPrimitive
?.content == "true",
)
}
@Test
fun testOpenRequestsReplayUsesTheLiveServerRequestDispatcher() =
runBlocking {