File tree Expand file tree Collapse file tree 4 files changed +88
-2
lines changed
commonMain/kotlin/com/agentclientprotocol/rpc
commonTest/kotlin/com/agentclientprotocol/rpc
acp/src/commonMain/kotlin/com/agentclientprotocol/transport Expand file tree Collapse file tree 4 files changed +88
-2
lines changed Original file line number Diff line number Diff line change @@ -12,5 +12,13 @@ kotlin {
1212 api(libs.kotlinx.collections.immutable)
1313 }
1414 }
15+
16+ commonTest {
17+ dependencies {
18+ implementation(kotlin(" test" ))
19+ implementation(libs.kotlinx.coroutines.test)
20+ }
21+ }
22+
1523 }
1624}
Original file line number Diff line number Diff line change @@ -4,6 +4,7 @@ package com.agentclientprotocol.rpc
44
55import kotlinx.serialization.ExperimentalSerializationApi
66import kotlinx.serialization.Serializable
7+ import kotlinx.serialization.SerializationException
78import kotlinx.serialization.json.Json
89import kotlinx.serialization.json.JsonElement
910import kotlinx.serialization.json.JsonObject
@@ -103,7 +104,17 @@ public val ACPJson: Json by lazy {
103104 * - Notification: has "method" but no "id"
104105 */
105106public fun decodeJsonRpcMessage (jsonString : String ): JsonRpcMessage {
106- val element = ACPJson .parseToJsonElement(jsonString)
107+ val element = try {
108+ ACPJson .parseToJsonElement(jsonString)
109+ } catch (e: SerializationException ) {
110+ // maybe there is some garbage output at the beginning of the like, try to find where JSON starts
111+ val jsonStart = jsonString.indexOfFirst { it == ' {' }
112+ if (jsonStart == - 1 ) {
113+ throw e
114+ }
115+ val jsonStartTrimmed = jsonString.substring(jsonStart)
116+ ACPJson .parseToJsonElement(jsonStartTrimmed)
117+ }
107118 require(element is JsonObject ) { " Expected JSON object" }
108119
109120 val hasId = element.containsKey(" id" )
Original file line number Diff line number Diff line change 1+ package com.agentclientprotocol.rpc
2+
3+ import kotlinx.serialization.SerializationException
4+ import kotlinx.serialization.json.jsonObject
5+ import kotlinx.serialization.json.jsonPrimitive
6+ import kotlin.test.Test
7+ import kotlin.test.assertEquals
8+ import kotlin.test.assertTrue
9+ import kotlin.test.fail
10+
11+ class JsonDecodeTest {
12+
13+ @Test
14+ fun testDecode () {
15+ val result = decodeJsonRpcMessage(
16+ """
17+ {
18+ "jsonrpc": "2.0",
19+ "id": 5,
20+ "result": {
21+ "outcome": {
22+ "outcome": "selected",
23+ "optionId": "allow-once"
24+ }
25+ }
26+ }
27+ """ .trimIndent()
28+ )
29+ assertTrue(result is JsonRpcResponse )
30+ }
31+
32+ @Test
33+ fun testDecodeWithPrefix () {
34+ val result = decodeJsonRpcMessage(
35+ """
36+ asdfasdfasdf(){
37+ "jsonrpc": "2.0",
38+ "id": 5,
39+ "result": {
40+ "outcome": {
41+ "outcome": "selected",
42+ "optionId": "allow-once"
43+ }
44+ }
45+ }
46+ """ .trimIndent()
47+ )
48+ assertTrue(result is JsonRpcResponse )
49+ assertEquals(" selected" , result.result!! .jsonObject[" outcome" ]!! .jsonObject[" outcome" ]!! .jsonPrimitive.content)
50+ }
51+
52+ @Test
53+ fun testDecodeError () {
54+ try {
55+ decodeJsonRpcMessage("""
56+ asdfasdfas
57+ "outcome": {
58+ "outcome": "selected",
59+ "optionId": "allow-once"
60+ }
61+ }
62+ }
63+ """ .trimIndent())
64+ fail(" Exception expected" )
65+ } catch (_: SerializationException ) {}
66+ }
67+
68+ }
Original file line number Diff line number Diff line change @@ -60,7 +60,6 @@ public class StdioTransport(
6060 decodeJsonRpcMessage(line)
6161 } catch (t: Throwable ) {
6262 logger.trace(t) { " Failed to decode JSON message: $line " }
63- fireError(t)
6463 continue
6564 }
6665 logger.trace { " Sending message to channel: $jsonRpcMessage " }
You can’t perform that action at this time.
0 commit comments