From e48513d51277f3042a57c3ba032b69af869abd5a Mon Sep 17 00:00:00 2001 From: Ayaan Zaidi Date: Fri, 27 Feb 2026 09:28:59 +0530 Subject: [PATCH] fix(android): scale invoke result ack timeout to invoke budget --- .../android/gateway/GatewaySession.kt | 22 +++++++++++++--- .../GatewaySessionInvokeTimeoutTest.kt | 25 +++++++++++++++++++ 2 files changed, 43 insertions(+), 4 deletions(-) create mode 100644 apps/android/app/src/test/java/ai/openclaw/android/gateway/GatewaySessionInvokeTimeoutTest.kt diff --git a/apps/android/app/src/main/java/ai/openclaw/android/gateway/GatewaySession.kt b/apps/android/app/src/main/java/ai/openclaw/android/gateway/GatewaySession.kt index 7c8b13ec396..5262899f09b 100644 --- a/apps/android/app/src/main/java/ai/openclaw/android/gateway/GatewaySession.kt +++ b/apps/android/app/src/main/java/ai/openclaw/android/gateway/GatewaySession.kt @@ -501,11 +501,16 @@ class GatewaySession( } catch (err: Throwable) { invokeErrorFromThrowable(err) } - sendInvokeResult(id, nodeId, result) + sendInvokeResult(id, nodeId, result, timeoutMs) } } - private suspend fun sendInvokeResult(id: String, nodeId: String, result: InvokeResult) { + private suspend fun sendInvokeResult( + id: String, + nodeId: String, + result: InvokeResult, + invokeTimeoutMs: Long?, + ) { val parsedPayload = result.payloadJson?.let { parseJsonOrNull(it) } val params = buildJsonObject { @@ -527,10 +532,14 @@ class GatewaySession( ) } } + val ackTimeoutMs = resolveInvokeResultAckTimeoutMs(invokeTimeoutMs) try { - request("node.invoke.result", params, timeoutMs = 15_000) + request("node.invoke.result", params, timeoutMs = ackTimeoutMs) } catch (err: Throwable) { - Log.w(loggerTag, "node.invoke.result failed: ${err.message ?: err::class.java.simpleName}") + Log.w( + loggerTag, + "node.invoke.result failed (ackTimeoutMs=$ackTimeoutMs): ${err.message ?: err::class.java.simpleName}", + ) } } @@ -687,3 +696,8 @@ private fun parseJsonOrNull(payload: String): JsonElement? { null } } + +internal fun resolveInvokeResultAckTimeoutMs(invokeTimeoutMs: Long?): Long { + val normalized = invokeTimeoutMs?.takeIf { it > 0L } ?: 15_000L + return normalized.coerceIn(15_000L, 120_000L) +} diff --git a/apps/android/app/src/test/java/ai/openclaw/android/gateway/GatewaySessionInvokeTimeoutTest.kt b/apps/android/app/src/test/java/ai/openclaw/android/gateway/GatewaySessionInvokeTimeoutTest.kt new file mode 100644 index 00000000000..680f3209280 --- /dev/null +++ b/apps/android/app/src/test/java/ai/openclaw/android/gateway/GatewaySessionInvokeTimeoutTest.kt @@ -0,0 +1,25 @@ +package ai.openclaw.android.gateway + +import org.junit.Assert.assertEquals +import org.junit.Test + +class GatewaySessionInvokeTimeoutTest { + @Test + fun resolveInvokeResultAckTimeoutMs_usesFloorWhenMissingOrTooSmall() { + assertEquals(15_000L, resolveInvokeResultAckTimeoutMs(null)) + assertEquals(15_000L, resolveInvokeResultAckTimeoutMs(0L)) + assertEquals(15_000L, resolveInvokeResultAckTimeoutMs(5_000L)) + } + + @Test + fun resolveInvokeResultAckTimeoutMs_usesInvokeBudgetWithinBounds() { + assertEquals(30_000L, resolveInvokeResultAckTimeoutMs(30_000L)) + assertEquals(90_000L, resolveInvokeResultAckTimeoutMs(90_000L)) + } + + @Test + fun resolveInvokeResultAckTimeoutMs_capsAtUpperBound() { + assertEquals(120_000L, resolveInvokeResultAckTimeoutMs(121_000L)) + assertEquals(120_000L, resolveInvokeResultAckTimeoutMs(Long.MAX_VALUE)) + } +}