feat: add ToolCallAssessedEvent for plane-2 tool-call assessment

This commit is contained in:
2026-05-31 03:37:49 +04:00
parent d3ce310100
commit 2964849bb2
4 changed files with 88 additions and 0 deletions
+1
View File
@@ -7,5 +7,6 @@ plugins {
dependencies {
implementation "org.jetbrains.kotlinx:kotlinx-datetime"
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:$kotlinx_coroutines_version"
testImplementation "org.jetbrains.kotlin:kotlin-test"
}
tasks.named("koverVerify").configure { enabled = false }
@@ -0,0 +1,34 @@
package com.correx.core.events.events
import com.correx.core.events.risk.RiskAction
import com.correx.core.events.types.SessionId
import com.correx.core.events.types.StageId
import com.correx.core.events.types.ToolInvocationId
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
@Serializable
data class AssessedIssue(
val code: String,
val message: String,
val severity: String,
)
@Serializable
data class ToolCallObservation(
val ruleCode: String,
val facts: Map<String, String> = emptyMap(),
)
@Serializable
@SerialName("ToolCallAssessed")
data class ToolCallAssessedEvent(
val invocationId: ToolInvocationId,
val sessionId: SessionId,
val stageId: StageId,
val toolName: String,
val issues: List<AssessedIssue> = emptyList(),
val observations: List<ToolCallObservation> = emptyList(),
val disposition: RiskAction,
val timestampMs: Long,
) : EventPayload
@@ -25,6 +25,7 @@ import com.correx.core.events.events.RiskAssessedEvent
import com.correx.core.events.events.StageCompletedEvent
import com.correx.core.events.events.StageFailedEvent
import com.correx.core.events.events.SteeringNoteAddedEvent
import com.correx.core.events.events.ToolCallAssessedEvent
import com.correx.core.events.events.ToolExecutionCompletedEvent
import com.correx.core.events.events.ToolExecutionFailedEvent
import com.correx.core.events.events.ToolExecutionRejectedEvent
@@ -49,6 +50,7 @@ val eventModule = SerializersModule {
subclass(ToolExecutionCompletedEvent::class)
subclass(ToolExecutionFailedEvent::class)
subclass(ToolExecutionRejectedEvent::class)
subclass(ToolCallAssessedEvent::class)
subclass(SteeringNoteAddedEvent::class)
subclass(StageFailedEvent::class)
subclass(StageCompletedEvent::class)
@@ -0,0 +1,51 @@
package com.correx.core.events.serialization
import com.correx.core.events.events.AssessedIssue
import com.correx.core.events.events.EventPayload
import com.correx.core.events.events.ToolCallAssessedEvent
import com.correx.core.events.events.ToolCallObservation
import com.correx.core.events.risk.RiskAction
import com.correx.core.events.types.SessionId
import com.correx.core.events.types.StageId
import com.correx.core.events.types.ToolInvocationId
import kotlin.test.Test
import kotlin.test.assertEquals
import kotlin.test.assertTrue
class ToolCallAssessedEventSerializationTest {
private val sample = ToolCallAssessedEvent(
invocationId = ToolInvocationId("inv-1"),
sessionId = SessionId("sess-1"),
stageId = StageId("stage-1"),
toolName = "file_write",
issues = listOf(AssessedIssue("PATH_OUTSIDE_WORKSPACE", "outside: /tmp/x", "WARNING")),
observations = listOf(
ToolCallObservation(
ruleCode = "PATH_CONTAINMENT",
facts = mapOf("path" to "/tmp/x", "exists" to "false", "inWorkspace" to "false", "privileged" to "false"),
),
),
disposition = RiskAction.PROMPT_USER,
timestampMs = 42L,
)
@Test
fun `round-trips as polymorphic EventPayload`() {
val encoded = eventJson.encodeToString(EventPayload.serializer(), sample)
assertTrue(encoded.contains("\"type\":\"ToolCallAssessed\""), "SerialName must be present: $encoded")
val decoded = eventJson.decodeFromString(EventPayload.serializer(), encoded)
assertEquals(sample, decoded)
}
@Test
fun `decodes hand-written ToolCallAssessed JSON`() {
val json = """
{"type":"ToolCallAssessed","invocationId":"inv-9","sessionId":"s","stageId":"st",
"toolName":"shell","issues":[],"observations":[],"disposition":"PROCEED","timestampMs":1}
""".trimIndent()
val decoded = eventJson.decodeFromString(EventPayload.serializer(), json)
assertTrue(decoded is ToolCallAssessedEvent)
assertEquals(RiskAction.PROCEED, (decoded as ToolCallAssessedEvent).disposition)
}
}