Skip to content

Commit f2e70ba

Browse files
authored
Pass serializers to methods exolicitly to avoid inline/reified APIs (#12)
Fixes #5
1 parent 67f1925 commit f2e70ba

File tree

5 files changed

+67
-41
lines changed

5 files changed

+67
-41
lines changed

acp-ktor-test/src/commonTest/kotlin/com/agentclientprotocol/ProtocolTest.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ abstract class ProtocolTest(protocolDriver: ProtocolDriver) : ProtocolDriver by
4242
val cancellationMessage = "Cancelled from test"
4343

4444
companion object {
45-
object TestMethod : AcpMethod.AcpRequestResponseMethod<TestRequest, TestResponse>("test/testRequest")
45+
object TestMethod : AcpMethod.AcpRequestResponseMethod<TestRequest, TestResponse>("test/testRequest", TestRequest.serializer(), TestResponse.serializer())
4646
}
4747

4848
@Test

acp-model/api/acp-model.api

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,19 +12,22 @@ public class com/agentclientprotocol/model/AcpMethod {
1212
}
1313

1414
public class com/agentclientprotocol/model/AcpMethod$AcpNotificationMethod : com/agentclientprotocol/model/AcpMethod {
15-
public fun <init> (Ljava/lang/String;)V
15+
public fun <init> (Ljava/lang/String;Lkotlinx/serialization/KSerializer;)V
16+
public final fun getSerializer ()Lkotlinx/serialization/KSerializer;
1617
}
1718

1819
public class com/agentclientprotocol/model/AcpMethod$AcpRequestResponseMethod : com/agentclientprotocol/model/AcpMethod {
19-
public fun <init> (Ljava/lang/String;)V
20+
public fun <init> (Ljava/lang/String;Lkotlinx/serialization/KSerializer;Lkotlinx/serialization/KSerializer;)V
21+
public final fun getRequestSerializer ()Lkotlinx/serialization/KSerializer;
22+
public final fun getResponseSerializer ()Lkotlinx/serialization/KSerializer;
2023
}
2124

2225
public class com/agentclientprotocol/model/AcpMethod$AcpSessionNotificationMethod : com/agentclientprotocol/model/AcpMethod$AcpNotificationMethod {
23-
public fun <init> (Ljava/lang/String;)V
26+
public fun <init> (Ljava/lang/String;Lkotlinx/serialization/KSerializer;)V
2427
}
2528

2629
public class com/agentclientprotocol/model/AcpMethod$AcpSessionRequestResponseMethod : com/agentclientprotocol/model/AcpMethod$AcpRequestResponseMethod {
27-
public fun <init> (Ljava/lang/String;)V
30+
public fun <init> (Ljava/lang/String;Lkotlinx/serialization/KSerializer;Lkotlinx/serialization/KSerializer;)V
2831
}
2932

3033
public final class com/agentclientprotocol/model/AcpMethod$AgentMethods {

acp-model/src/commonMain/kotlin/com/agentclientprotocol/model/Methods.kt

Lines changed: 36 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33
package com.agentclientprotocol.model
44

55
import com.agentclientprotocol.rpc.MethodName
6+
import kotlinx.serialization.KSerializer
7+
import kotlinx.serialization.builtins.serializer
68

79
/**
810
* Base interface for ACP method enums.
@@ -11,47 +13,59 @@ import com.agentclientprotocol.rpc.MethodName
1113
*/
1214
public open class AcpMethod(public val methodName: MethodName) {
1315

14-
public open class AcpRequestResponseMethod<TRequest: AcpRequest, TResponse: AcpResponse>(method: String) : AcpMethod(MethodName(method))
16+
public open class AcpRequestResponseMethod<TRequest: AcpRequest, TResponse: AcpResponse>(
17+
method: String,
18+
public val requestSerializer: KSerializer<TRequest>,
19+
public val responseSerializer: KSerializer<TResponse>
20+
) : AcpMethod(MethodName(method))
1521

16-
public open class AcpSessionRequestResponseMethod<TRequest, TResponse: AcpResponse>(method: String) : AcpRequestResponseMethod<TRequest, TResponse>(method)
22+
public open class AcpSessionRequestResponseMethod<TRequest, TResponse: AcpResponse>(method: String,
23+
requestSerializer: KSerializer<TRequest>,
24+
responseSerializer: KSerializer<TResponse>
25+
) : AcpRequestResponseMethod<TRequest, TResponse>(method, requestSerializer, responseSerializer)
1726
where TRequest : AcpRequest, TRequest : AcpWithSessionId
1827

19-
public open class AcpNotificationMethod<TNotification: AcpNotification>(method: String) : AcpMethod(MethodName(method))
28+
public open class AcpNotificationMethod<TNotification: AcpNotification>(
29+
method: String,
30+
public val serializer: KSerializer<TNotification>,
31+
) : AcpMethod(MethodName(method))
2032

21-
public open class AcpSessionNotificationMethod<TNotification>(method: String) : AcpNotificationMethod<TNotification>(method)
33+
public open class AcpSessionNotificationMethod<TNotification>(method: String,
34+
serializer: KSerializer<TNotification>
35+
) : AcpNotificationMethod<TNotification>(method, serializer)
2236
where TNotification : AcpNotification, TNotification : AcpWithSessionId
2337

2438
public object MetaMethods {
25-
public object CancelRequest : AcpNotificationMethod<CancelRequestNotification>("\$/cancelRequest")
39+
public object CancelRequest : AcpNotificationMethod<CancelRequestNotification>("\$/cancelRequest", CancelRequestNotification.serializer())
2640
}
2741

2842
public object AgentMethods {
2943
// Agent-side operations (methods that agents can call on clients)
30-
public object Initialize : AcpRequestResponseMethod<InitializeRequest, InitializeResponse>("initialize")
31-
public object Authenticate : AcpRequestResponseMethod<AuthenticateRequest, AuthenticateResponse>("authenticate")
32-
public object SessionNew : AcpRequestResponseMethod<NewSessionRequest, NewSessionResponse>("session/new")
33-
public object SessionLoad : AcpRequestResponseMethod<LoadSessionRequest, LoadSessionResponse>("session/load")
44+
public object Initialize : AcpRequestResponseMethod<InitializeRequest, InitializeResponse>("initialize", InitializeRequest.serializer(), InitializeResponse.serializer())
45+
public object Authenticate : AcpRequestResponseMethod<AuthenticateRequest, AuthenticateResponse>("authenticate", AuthenticateRequest.serializer(), AuthenticateResponse.serializer())
46+
public object SessionNew : AcpRequestResponseMethod<NewSessionRequest, NewSessionResponse>("session/new", NewSessionRequest.serializer(), NewSessionResponse.serializer())
47+
public object SessionLoad : AcpRequestResponseMethod<LoadSessionRequest, LoadSessionResponse>("session/load", LoadSessionRequest.serializer(), LoadSessionResponse.serializer())
3448

3549
// session specific
36-
public object SessionPrompt : AcpSessionRequestResponseMethod<PromptRequest, PromptResponse>("session/prompt")
37-
public object SessionCancel : AcpSessionNotificationMethod<CancelNotification>("session/cancel")
38-
public object SessionSetMode : AcpSessionRequestResponseMethod<SetSessionModeRequest, SetSessionModeResponse>("session/set_mode")
39-
public object SessionSetModel : AcpSessionRequestResponseMethod<SetSessionModelRequest, SetSessionModelResponse>("session/set_model")
50+
public object SessionPrompt : AcpSessionRequestResponseMethod<PromptRequest, PromptResponse>("session/prompt", PromptRequest.serializer(), PromptResponse.serializer())
51+
public object SessionCancel : AcpSessionNotificationMethod<CancelNotification>("session/cancel", CancelNotification.serializer())
52+
public object SessionSetMode : AcpSessionRequestResponseMethod<SetSessionModeRequest, SetSessionModeResponse>("session/set_mode", SetSessionModeRequest.serializer(), SetSessionModeResponse.serializer())
53+
public object SessionSetModel : AcpSessionRequestResponseMethod<SetSessionModelRequest, SetSessionModelResponse>("session/set_model", SetSessionModelRequest.serializer(), SetSessionModelResponse.serializer())
4054
}
4155

4256
public object ClientMethods {
4357
// Client-side operations (methods that clients can call on agents)
44-
public object SessionRequestPermission : AcpSessionRequestResponseMethod<RequestPermissionRequest, RequestPermissionResponse>("session/request_permission")
45-
public object SessionUpdate : AcpSessionNotificationMethod<SessionNotification>("session/update")
58+
public object SessionRequestPermission : AcpSessionRequestResponseMethod<RequestPermissionRequest, RequestPermissionResponse>("session/request_permission", RequestPermissionRequest.serializer(), RequestPermissionResponse.serializer())
59+
public object SessionUpdate : AcpSessionNotificationMethod<SessionNotification>("session/update", SessionNotification.serializer())
4660

4761
// extensions
48-
public object FsReadTextFile : AcpSessionRequestResponseMethod<ReadTextFileRequest, ReadTextFileResponse>("fs/read_text_file")
49-
public object FsWriteTextFile : AcpSessionRequestResponseMethod<WriteTextFileRequest, WriteTextFileResponse>("fs/write_text_file")
50-
public object TerminalCreate : AcpSessionRequestResponseMethod<CreateTerminalRequest, CreateTerminalResponse>("terminal/create")
51-
public object TerminalOutput : AcpSessionRequestResponseMethod<TerminalOutputRequest, TerminalOutputResponse>("terminal/output")
52-
public object TerminalRelease : AcpSessionRequestResponseMethod<ReleaseTerminalRequest, ReleaseTerminalResponse>("terminal/release")
53-
public object TerminalWaitForExit : AcpSessionRequestResponseMethod<WaitForTerminalExitRequest, WaitForTerminalExitResponse>("terminal/wait_for_exit")
54-
public object TerminalKill : AcpSessionRequestResponseMethod<KillTerminalCommandRequest, KillTerminalCommandResponse>("terminal/kill")
62+
public object FsReadTextFile : AcpSessionRequestResponseMethod<ReadTextFileRequest, ReadTextFileResponse>("fs/read_text_file", ReadTextFileRequest.serializer(), ReadTextFileResponse.serializer())
63+
public object FsWriteTextFile : AcpSessionRequestResponseMethod<WriteTextFileRequest, WriteTextFileResponse>("fs/write_text_file", WriteTextFileRequest.serializer(), WriteTextFileResponse.serializer())
64+
public object TerminalCreate : AcpSessionRequestResponseMethod<CreateTerminalRequest, CreateTerminalResponse>("terminal/create", CreateTerminalRequest.serializer(), CreateTerminalResponse.serializer())
65+
public object TerminalOutput : AcpSessionRequestResponseMethod<TerminalOutputRequest, TerminalOutputResponse>("terminal/output", TerminalOutputRequest.serializer(), TerminalOutputResponse.serializer())
66+
public object TerminalRelease : AcpSessionRequestResponseMethod<ReleaseTerminalRequest, ReleaseTerminalResponse>("terminal/release", ReleaseTerminalRequest.serializer(), ReleaseTerminalResponse.serializer())
67+
public object TerminalWaitForExit : AcpSessionRequestResponseMethod<WaitForTerminalExitRequest, WaitForTerminalExitResponse>("terminal/wait_for_exit", WaitForTerminalExitRequest.serializer(), WaitForTerminalExitResponse.serializer())
68+
public object TerminalKill : AcpSessionRequestResponseMethod<KillTerminalCommandRequest, KillTerminalCommandResponse>("terminal/kill", KillTerminalCommandRequest.serializer(), KillTerminalCommandResponse.serializer())
5569
}
5670

5771

acp/api/acp.api

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -251,6 +251,15 @@ public class com/agentclientprotocol/protocol/ProtocolOptions {
251251

252252
public final class com/agentclientprotocol/protocol/Protocol_extensionsKt {
253253
public static final fun getJsonRpcRequest (Lkotlin/coroutines/CoroutineContext;)Lcom/agentclientprotocol/rpc/JsonRpcRequest;
254+
public static final fun invoke (Lcom/agentclientprotocol/model/AcpMethod$AcpNotificationMethod;Lcom/agentclientprotocol/protocol/RpcMethodsOperations;Lcom/agentclientprotocol/model/AcpNotification;)V
255+
public static final fun invoke (Lcom/agentclientprotocol/model/AcpMethod$AcpRequestResponseMethod;Lcom/agentclientprotocol/protocol/RpcMethodsOperations;Lcom/agentclientprotocol/model/AcpRequest;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
256+
public static final fun sendNotification (Lcom/agentclientprotocol/protocol/RpcMethodsOperations;Lcom/agentclientprotocol/model/AcpMethod$AcpNotificationMethod;Lcom/agentclientprotocol/model/AcpNotification;)V
257+
public static synthetic fun sendNotification$default (Lcom/agentclientprotocol/protocol/RpcMethodsOperations;Lcom/agentclientprotocol/model/AcpMethod$AcpNotificationMethod;Lcom/agentclientprotocol/model/AcpNotification;ILjava/lang/Object;)V
258+
public static final fun sendRequest (Lcom/agentclientprotocol/protocol/RpcMethodsOperations;Lcom/agentclientprotocol/model/AcpMethod$AcpRequestResponseMethod;Lcom/agentclientprotocol/model/AcpRequest;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
259+
public static final fun setNotificationHandler (Lcom/agentclientprotocol/protocol/RpcMethodsOperations;Lcom/agentclientprotocol/model/AcpMethod$AcpNotificationMethod;Lkotlin/coroutines/CoroutineContext;Lkotlin/jvm/functions/Function2;)V
260+
public static synthetic fun setNotificationHandler$default (Lcom/agentclientprotocol/protocol/RpcMethodsOperations;Lcom/agentclientprotocol/model/AcpMethod$AcpNotificationMethod;Lkotlin/coroutines/CoroutineContext;Lkotlin/jvm/functions/Function2;ILjava/lang/Object;)V
261+
public static final fun setRequestHandler (Lcom/agentclientprotocol/protocol/RpcMethodsOperations;Lcom/agentclientprotocol/model/AcpMethod$AcpRequestResponseMethod;Lkotlin/coroutines/CoroutineContext;Lkotlin/jvm/functions/Function2;)V
262+
public static synthetic fun setRequestHandler$default (Lcom/agentclientprotocol/protocol/RpcMethodsOperations;Lcom/agentclientprotocol/model/AcpMethod$AcpRequestResponseMethod;Lkotlin/coroutines/CoroutineContext;Lkotlin/jvm/functions/Function2;ILjava/lang/Object;)V
254263
}
255264

256265
public final class com/agentclientprotocol/protocol/RequestTimeoutException : java/lang/Exception {

acp/src/commonMain/kotlin/com/agentclientprotocol/protocol/Protocol.extensions.kt

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -17,59 +17,59 @@ import kotlin.coroutines.EmptyCoroutineContext
1717
/**
1818
* Send a request and wait for the response.
1919
*/
20-
public suspend inline fun <reified TRequest : AcpRequest, reified TResponse : AcpResponse> RpcMethodsOperations.sendRequest(
20+
public suspend fun <TRequest : AcpRequest, TResponse : AcpResponse> RpcMethodsOperations.sendRequest(
2121
method: AcpMethod.AcpRequestResponseMethod<TRequest, TResponse>,
2222
request: TRequest?
2323
): TResponse {
24-
val params = request?.let { ACPJson.encodeToJsonElement(request) }
24+
val params = request?.let { ACPJson.encodeToJsonElement(method.requestSerializer, request) }
2525
val responseJson = this.sendRequestRaw(method.methodName, params)
26-
return ACPJson.decodeFromJsonElement(responseJson)
26+
return ACPJson.decodeFromJsonElement(method.responseSerializer, responseJson)
2727
}
2828

2929
/**
3030
* Send a notification (no response expected).
3131
*/
32-
public inline fun <reified TNotification: AcpNotification> RpcMethodsOperations.sendNotification(
32+
public fun <TNotification: AcpNotification> RpcMethodsOperations.sendNotification(
3333
method: AcpMethod.AcpNotificationMethod<TNotification>,
3434
notification: TNotification? = null,
3535
) {
36-
val params = notification?.let { ACPJson.encodeToJsonElement(notification) }
36+
val params = notification?.let { ACPJson.encodeToJsonElement(method.serializer, notification) }
3737
this.sendNotificationRaw(method, params)
3838
}
3939

4040
/**
4141
* Register a handler for incoming requests.
4242
*/
43-
public inline fun<reified TRequest : AcpRequest, reified TResponse : AcpResponse> RpcMethodsOperations.setRequestHandler(
43+
public fun<TRequest : AcpRequest, TResponse : AcpResponse> RpcMethodsOperations.setRequestHandler(
4444
method: AcpMethod.AcpRequestResponseMethod<TRequest, TResponse>,
4545
additionalContext: CoroutineContext = EmptyCoroutineContext,
46-
noinline handler: suspend (TRequest) -> TResponse
46+
handler: suspend (TRequest) -> TResponse
4747
) {
4848
this.setRequestHandlerRaw(method, additionalContext) { request ->
49-
val requestParams = ACPJson.decodeFromJsonElement<TRequest>(request.params ?: JsonNull)
49+
val requestParams = ACPJson.decodeFromJsonElement(method.requestSerializer, request.params ?: JsonNull)
5050
val responseObject = handler(requestParams)
51-
ACPJson.encodeToJsonElement(responseObject)
51+
ACPJson.encodeToJsonElement(method.responseSerializer, responseObject)
5252
}
5353
}
5454
/**
5555
* Register a handler for incoming notifications.
5656
*/
57-
public inline fun<reified TNotification : AcpNotification> RpcMethodsOperations.setNotificationHandler(
57+
public fun<TNotification : AcpNotification> RpcMethodsOperations.setNotificationHandler(
5858
method: AcpMethod.AcpNotificationMethod<TNotification>,
5959
additionalContext: CoroutineContext = EmptyCoroutineContext,
60-
noinline handler: suspend (TNotification) -> Unit
60+
handler: suspend (TNotification) -> Unit
6161
) {
6262
this.setNotificationHandlerRaw(method, additionalContext) { notification ->
63-
val notificationParams = ACPJson.decodeFromJsonElement<TNotification>(notification.params ?: JsonNull)
63+
val notificationParams = ACPJson.decodeFromJsonElement(method.serializer, notification.params ?: JsonNull)
6464
handler(notificationParams)
6565
}
6666
}
6767

68-
public suspend inline operator fun <reified TRequest: AcpRequest, reified TResponse: AcpResponse> AcpMethod.AcpRequestResponseMethod<TRequest, TResponse>.invoke(rpc: RpcMethodsOperations, request: TRequest): TResponse {
68+
public suspend operator fun <TRequest: AcpRequest, TResponse: AcpResponse> AcpMethod.AcpRequestResponseMethod<TRequest, TResponse>.invoke(rpc: RpcMethodsOperations, request: TRequest): TResponse {
6969
return rpc.sendRequest(this, request)
7070
}
7171

72-
public inline operator fun <reified TNotification : AcpNotification> AcpMethod.AcpNotificationMethod<TNotification>.invoke(rpc: RpcMethodsOperations, notification: TNotification) {
72+
public operator fun <TNotification : AcpNotification> AcpMethod.AcpNotificationMethod<TNotification>.invoke(rpc: RpcMethodsOperations, notification: TNotification) {
7373
return rpc.sendNotification(this, notification)
7474
}
7575

0 commit comments

Comments
 (0)