feat(server): Task 3.4 — ServerMessage.Narration + DomainEventMapper arm

This commit is contained in:
2026-06-03 18:55:57 +04:00
parent 689020e161
commit da1aca411a
3 changed files with 40 additions and 0 deletions
@@ -30,6 +30,7 @@ import com.correx.core.events.events.ToolCallAssessedEvent
import com.correx.core.events.events.ToolInvocationRequestedEvent
import com.correx.core.events.events.TransitionExecutedEvent
import com.correx.core.events.events.WorkflowCompletedEvent
import com.correx.core.events.events.RouterNarrationEvent
import com.correx.core.events.events.WorkflowFailedEvent
import com.correx.core.events.risk.RiskAction
import com.correx.core.events.types.ArtifactId
@@ -205,6 +206,15 @@ suspend fun domainEventToServerMessage(
sequence = seq,
sessionSequence = sessionSequence,
)
is RouterNarrationEvent -> ServerMessage.Narration(
sessionId = p.sessionId,
content = p.content,
stageId = p.stageId,
latencyMs = p.latencyMs,
totalTokens = p.tokensUsed?.totalTokens,
sequence = seq,
sessionSequence = sessionSequence,
)
is ArtifactCreatedEvent -> ServerMessage.ArtifactCreated(
sessionId = p.sessionId,
stageId = p.stageId,
@@ -362,6 +362,18 @@ sealed interface ServerMessage {
// -- Router --
@Serializable
@SerialName("router.narration")
data class Narration(
val sessionId: SessionId,
val content: String,
val stageId: String? = null,
val latencyMs: Long? = null,
val totalTokens: Int? = null,
override val sequence: Long,
override val sessionSequence: Long,
) : ServerMessage, SessionMessage
@Serializable
@SerialName("router.response")
data class RouterResponseMessage(
@@ -107,6 +107,24 @@ class ServerMessageSerializationTest {
assert(jsonStr.contains("\"lastSessionSequence\":15")) { "expected lastSessionSequence=15" }
}
@Test
fun `Narration encodes type content stageId latencyMs and totalTokens`() {
val msg = ServerMessage.Narration(
sessionId = SessionId("sess-1"),
content = "moving on",
stageId = "validate",
latencyMs = 2000,
totalTokens = 120,
sequence = 10,
sessionSequence = 5,
)
val jsonStr = ProtocolSerializer.encodeServerMessage(msg)
assert(jsonStr.contains("\"type\":\"router.narration\"")) { "expected type=router.narration" }
assert(jsonStr.contains("\"content\":\"moving on\"")) { "expected content" }
assert(jsonStr.contains("\"latencyMs\":2000")) { "expected latencyMs=2000" }
assert(jsonStr.contains("\"totalTokens\":120")) { "expected totalTokens=120" }
}
@Test
fun `SessionAnnounced round-trips through JSON`() {
val original = ServerMessage.SessionAnnounced(