This commit is contained in:
11 files changed
+611
-63
No files matched your search
@@ -3,6 +3,8 @@ package com.m57.hermescontrol.data.ws
|
||||
import com.m57.hermescontrol.data.model.ActiveSessionsResponse
|
||||
import com.m57.hermescontrol.data.model.LiveSessionSnapshot
|
||||
import com.m57.hermescontrol.data.model.SessionLiveStatus
|
||||
import com.m57.hermescontrol.data.ws.contract.RpcMethods
|
||||
import com.m57.hermescontrol.data.ws.contract.SessionActiveListParams
|
||||
import kotlinx.coroutines.CancellationException
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
@@ -148,8 +150,8 @@ object SessionLiveStatusDecoder {
|
||||
* Production implementation of [SessionLiveStatusSource] backed by [HermesWsClient].
|
||||
*/
|
||||
class HermesSessionLiveStatusSource(
|
||||
private val rpcRequest: suspend (method: String, params: Map<String, Any>) -> Any? = { method, params ->
|
||||
HermesWsClient.request(method, params).await()
|
||||
private val rpcRequest: suspend () -> Any? = {
|
||||
HermesWsClient.call(RpcMethods.SESSION_ACTIVE_LIST, SessionActiveListParams())
|
||||
},
|
||||
eventsProvider: () -> Flow<WsEvent> = { HermesWsClient.events },
|
||||
connectionStatusProvider: () -> StateFlow<ConnectionStatus> = { HermesWsClient.connectionStatus },
|
||||
@@ -159,7 +161,7 @@ class HermesSessionLiveStatusSource(
|
||||
|
||||
override suspend fun fetchActiveSessionsSnapshot(): LiveSessionSnapshot? =
|
||||
try {
|
||||
val raw = rpcRequest(WsMethods.SESSION_ACTIVE_LIST, emptyMap())
|
||||
val raw = rpcRequest()
|
||||
SessionLiveStatusDecoder.decodeSnapshot(raw)
|
||||
} catch (e: CancellationException) {
|
||||
throw e
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
package com.m57.hermescontrol.data.ws.contract
|
||||
|
||||
import kotlinx.serialization.SerialName
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
@Serializable
|
||||
data class ApprovalPendingParams(
|
||||
@SerialName("session_id") val sessionId: String,
|
||||
)
|
||||
|
||||
@Serializable
|
||||
data class ApprovalReceivedParams(
|
||||
@SerialName("session_id") val sessionId: String,
|
||||
@SerialName("request_id") val requestId: String,
|
||||
)
|
||||
|
||||
@Serializable
|
||||
data class ApprovalRespondParams(
|
||||
@SerialName("session_id") val sessionId: String,
|
||||
@SerialName("choice") val choice: String? = null,
|
||||
@SerialName("all") val all: Boolean? = null,
|
||||
@SerialName("request_id") val requestId: String? = null,
|
||||
)
|
||||
|
||||
@Serializable
|
||||
data class SessionActiveListParams(
|
||||
@SerialName("current_session_id") val currentSessionId: String? = null,
|
||||
)
|
||||
@@ -10,6 +10,14 @@ class RpcMethod<P, R>(
|
||||
val result: KSerializer<R>,
|
||||
)
|
||||
|
||||
interface TypedRpcSender {
|
||||
fun <P> send(
|
||||
method: RpcMethod<P, *>,
|
||||
params: P,
|
||||
onSent: ((String) -> Unit)? = null,
|
||||
): String
|
||||
}
|
||||
|
||||
object RpcMethods {
|
||||
val SESSION_EVENTS_SINCE: RpcMethod<SessionEventsSinceParams, SessionEventsSinceResult> =
|
||||
RpcMethod(
|
||||
@@ -110,6 +118,38 @@ object RpcMethods {
|
||||
PromptBtwResult.serializer(),
|
||||
)
|
||||
|
||||
// Passthrough JsonElement results: callers keep their existing legacy parsers.
|
||||
val APPROVAL_PENDING: RpcMethod<ApprovalPendingParams, JsonElement> =
|
||||
RpcMethod(
|
||||
WsMethods.APPROVAL_PENDING,
|
||||
ApprovalPendingParams.serializer(),
|
||||
JsonElement.serializer(),
|
||||
)
|
||||
|
||||
// Passthrough JsonElement results: callers keep their existing legacy parsers.
|
||||
val APPROVAL_RECEIVED: RpcMethod<ApprovalReceivedParams, JsonElement> =
|
||||
RpcMethod(
|
||||
WsMethods.APPROVAL_RECEIVED,
|
||||
ApprovalReceivedParams.serializer(),
|
||||
JsonElement.serializer(),
|
||||
)
|
||||
|
||||
// Passthrough JsonElement results: callers keep their existing legacy parsers.
|
||||
val APPROVAL_RESPOND: RpcMethod<ApprovalRespondParams, JsonElement> =
|
||||
RpcMethod(
|
||||
WsMethods.APPROVAL_RESPOND,
|
||||
ApprovalRespondParams.serializer(),
|
||||
JsonElement.serializer(),
|
||||
)
|
||||
|
||||
// Passthrough JsonElement results: callers keep their existing legacy parsers.
|
||||
val SESSION_ACTIVE_LIST: RpcMethod<SessionActiveListParams, JsonElement> =
|
||||
RpcMethod(
|
||||
WsMethods.SESSION_ACTIVE_LIST,
|
||||
SessionActiveListParams.serializer(),
|
||||
JsonElement.serializer(),
|
||||
)
|
||||
|
||||
/** Every typed method. GatewayContractTest iterates this; future migrations append here. */
|
||||
val all: List<RpcMethod<*, *>> =
|
||||
listOf(
|
||||
@@ -127,5 +167,9 @@ object RpcMethods {
|
||||
SESSION_CONTEXT_BREAKDOWN,
|
||||
SESSION_USAGE,
|
||||
PROMPT_BTW,
|
||||
APPROVAL_PENDING,
|
||||
APPROVAL_RECEIVED,
|
||||
APPROVAL_RESPOND,
|
||||
SESSION_ACTIVE_LIST,
|
||||
)
|
||||
}
|
||||
@@ -2,6 +2,11 @@ package com.m57.hermescontrol.ui.chat
|
||||
|
||||
import com.m57.hermescontrol.data.ws.WsEvent
|
||||
import com.m57.hermescontrol.data.ws.WsMethods
|
||||
import com.m57.hermescontrol.data.ws.contract.ApprovalPendingParams
|
||||
import com.m57.hermescontrol.data.ws.contract.ApprovalReceivedParams
|
||||
import com.m57.hermescontrol.data.ws.contract.ApprovalRespondParams
|
||||
import com.m57.hermescontrol.data.ws.contract.RpcMethods
|
||||
import com.m57.hermescontrol.data.ws.contract.TypedRpcSender
|
||||
import kotlinx.coroutines.CoroutineDispatcher
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
@@ -22,7 +27,7 @@ class ChatApprovalsDelegate(
|
||||
private val ioDispatcher: CoroutineDispatcher,
|
||||
private val uiState: MutableStateFlow<ChatUiState>,
|
||||
private val runtimeSessionId: () -> String?,
|
||||
private val wsSend: (method: String, params: Map<String, Any>, onSent: ((String) -> Unit)?) -> Unit,
|
||||
private val rpc: TypedRpcSender,
|
||||
private val trackRequest: (id: String, method: String) -> Unit,
|
||||
private val addSystemMessage: (text: String) -> Unit,
|
||||
private val respondToServerRequest: ((String, JsonElement) -> Unit)? = null,
|
||||
@@ -99,13 +104,12 @@ class ChatApprovalsDelegate(
|
||||
if (event.serverRequestId == null && requestId != null && sessionId != null) {
|
||||
scope.launch(ioDispatcher) {
|
||||
runCatching {
|
||||
wsSend(
|
||||
WsMethods.APPROVAL_RECEIVED,
|
||||
mapOf(
|
||||
"session_id" to sessionId,
|
||||
"request_id" to requestId,
|
||||
rpc.send(
|
||||
RpcMethods.APPROVAL_RECEIVED,
|
||||
ApprovalReceivedParams(
|
||||
sessionId = sessionId,
|
||||
requestId = requestId,
|
||||
),
|
||||
null,
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -115,9 +119,9 @@ class ChatApprovalsDelegate(
|
||||
fun replayPendingApproval(sessionId: String) {
|
||||
val targetSessionId = runtimeSessionId() ?: sessionId
|
||||
scope.launch(ioDispatcher) {
|
||||
wsSend(
|
||||
WsMethods.APPROVAL_PENDING,
|
||||
mapOf("session_id" to targetSessionId),
|
||||
rpc.send(
|
||||
RpcMethods.APPROVAL_PENDING,
|
||||
ApprovalPendingParams(sessionId = targetSessionId),
|
||||
) { id -> trackRequest(id, WsMethods.APPROVAL_PENDING) }
|
||||
}
|
||||
}
|
||||
@@ -190,16 +194,14 @@ class ChatApprovalsDelegate(
|
||||
)
|
||||
return@launch
|
||||
}
|
||||
val params =
|
||||
mutableMapOf<String, Any>(
|
||||
"session_id" to sessionId,
|
||||
"choice" to choice,
|
||||
"all" to false,
|
||||
)
|
||||
if (requestId != null) params["request_id"] = requestId
|
||||
wsSend(
|
||||
WsMethods.APPROVAL_RESPOND,
|
||||
params,
|
||||
rpc.send(
|
||||
RpcMethods.APPROVAL_RESPOND,
|
||||
ApprovalRespondParams(
|
||||
sessionId = sessionId,
|
||||
choice = choice,
|
||||
all = false,
|
||||
requestId = requestId,
|
||||
),
|
||||
) { id -> trackRequest(id, WsMethods.APPROVAL_RESPOND) }
|
||||
// The queue can hold more pendings — surface the next one.
|
||||
replayPendingApproval(sessionId)
|
||||
|
||||
@@ -38,6 +38,7 @@ import com.m57.hermescontrol.data.ws.JsonRpcError
|
||||
import com.m57.hermescontrol.data.ws.WsEvent
|
||||
import com.m57.hermescontrol.data.ws.WsMethods
|
||||
import com.m57.hermescontrol.data.ws.contract.PromptBtwParams
|
||||
import com.m57.hermescontrol.data.ws.contract.RpcMethod
|
||||
import com.m57.hermescontrol.data.ws.contract.RpcMethods
|
||||
import com.m57.hermescontrol.data.ws.contract.SessionBranchParams
|
||||
import com.m57.hermescontrol.data.ws.contract.SessionBranchWholeParams
|
||||
@@ -48,6 +49,7 @@ import com.m57.hermescontrol.data.ws.contract.SessionIdParams
|
||||
import com.m57.hermescontrol.data.ws.contract.SessionInterruptParams
|
||||
import com.m57.hermescontrol.data.ws.contract.SessionListParams
|
||||
import com.m57.hermescontrol.data.ws.contract.SessionResumeParams
|
||||
import com.m57.hermescontrol.data.ws.contract.TypedRpcSender
|
||||
import com.m57.hermescontrol.data.ws.toAny
|
||||
import com.m57.hermescontrol.data.ws.toJsonElement
|
||||
import com.m57.hermescontrol.notification.ReplyNotificationTracker
|
||||
@@ -711,7 +713,14 @@ class ChatViewModel(
|
||||
ioDispatcher = ioDispatcher,
|
||||
uiState = _uiState,
|
||||
runtimeSessionId = { runtimeSessionId },
|
||||
wsSend = { method, params, onSent -> wsClient.send(method, params, onSent) },
|
||||
rpc =
|
||||
object : TypedRpcSender {
|
||||
override fun <P> send(
|
||||
method: RpcMethod<P, *>,
|
||||
params: P,
|
||||
onSent: ((String) -> Unit)?,
|
||||
): String = wsClient.send(method, params, onSent)
|
||||
},
|
||||
trackRequest = { id, method -> trackRequest(id, method) },
|
||||
addSystemMessage = { text -> addSystemMessage(text) },
|
||||
respondToServerRequest = { id, result -> wsClient.respondToServerRequest(id, result) },
|
||||
|
||||
Reference in new issue
Block a user