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 18c2bf21368..9e932b7dec4 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 @@ -734,7 +734,7 @@ private fun parseJsonOrNull(payload: String): JsonElement? { } } -private fun replaceCanvasCapabilityInScopedHostUrl( +internal fun replaceCanvasCapabilityInScopedHostUrl( scopedUrl: String, capability: String, ): String? { @@ -742,7 +742,10 @@ private fun replaceCanvasCapabilityInScopedHostUrl( val markerStart = scopedUrl.indexOf(marker) if (markerStart < 0) return null val capabilityStart = markerStart + marker.length - val capabilityEnd = scopedUrl.indexOf("/", capabilityStart) + val slashEnd = scopedUrl.indexOf("/", capabilityStart).takeIf { it >= 0 } + val queryEnd = scopedUrl.indexOf("?", capabilityStart).takeIf { it >= 0 } + val fragmentEnd = scopedUrl.indexOf("#", capabilityStart).takeIf { it >= 0 } + val capabilityEnd = listOfNotNull(slashEnd, queryEnd, fragmentEnd).minOrNull() ?: scopedUrl.length if (capabilityEnd <= capabilityStart) return null return scopedUrl.substring(0, capabilityStart) + capability + scopedUrl.substring(capabilityEnd) } 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 index 680f3209280..cd08715c405 100644 --- 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 @@ -22,4 +22,26 @@ class GatewaySessionInvokeTimeoutTest { assertEquals(120_000L, resolveInvokeResultAckTimeoutMs(121_000L)) assertEquals(120_000L, resolveInvokeResultAckTimeoutMs(Long.MAX_VALUE)) } + + @Test + fun replaceCanvasCapabilityInScopedHostUrl_rewritesTerminalCapabilitySegment() { + assertEquals( + "http://127.0.0.1:18789/__openclaw__/cap/new-token", + replaceCanvasCapabilityInScopedHostUrl( + "http://127.0.0.1:18789/__openclaw__/cap/old-token", + "new-token", + ), + ) + } + + @Test + fun replaceCanvasCapabilityInScopedHostUrl_rewritesWhenQueryAndFragmentPresent() { + assertEquals( + "http://127.0.0.1:18789/__openclaw__/cap/new-token?a=1#frag", + replaceCanvasCapabilityInScopedHostUrl( + "http://127.0.0.1:18789/__openclaw__/cap/old-token?a=1#frag", + "new-token", + ), + ) + } }