diff --git a/core/events/src/main/kotlin/com/correx/core/events/events/SessionEvents.kt b/core/events/src/main/kotlin/com/correx/core/events/events/SessionEvents.kt index 6de620c8..817525fd 100644 --- a/core/events/src/main/kotlin/com/correx/core/events/events/SessionEvents.kt +++ b/core/events/src/main/kotlin/com/correx/core/events/events/SessionEvents.kt @@ -9,3 +9,11 @@ import kotlinx.serialization.Serializable data class ChatSessionStartedEvent( val sessionId: SessionId, ) : EventPayload + +@Serializable +@SerialName("SessionWorkspaceBound") +data class SessionWorkspaceBoundEvent( + val sessionId: SessionId, + val workspaceRoot: String, + val allowedPaths: List, +) : EventPayload diff --git a/core/events/src/main/kotlin/com/correx/core/events/serialization/Serialization.kt b/core/events/src/main/kotlin/com/correx/core/events/serialization/Serialization.kt index cd98fa5e..3fa3efb0 100644 --- a/core/events/src/main/kotlin/com/correx/core/events/serialization/Serialization.kt +++ b/core/events/src/main/kotlin/com/correx/core/events/serialization/Serialization.kt @@ -9,6 +9,7 @@ import com.correx.core.events.events.ArtifactValidatedEvent import com.correx.core.events.events.ArtifactValidatingEvent import com.correx.core.events.events.ChatSessionStartedEvent import com.correx.core.events.events.ChatTurnEvent +import com.correx.core.events.events.SessionWorkspaceBoundEvent import com.correx.core.events.events.ContextTruncatedEvent import com.correx.core.events.events.EventPayload import com.correx.core.events.events.FileWrittenEvent @@ -79,6 +80,7 @@ val eventModule = SerializersModule { subclass(RiskAssessedEvent::class) subclass(ChatSessionStartedEvent::class) subclass(ChatTurnEvent::class) + subclass(SessionWorkspaceBoundEvent::class) subclass(L3MemoryRetrievedEvent::class) subclass(ContextTruncatedEvent::class) } diff --git a/core/events/src/test/kotlin/com/correx/core/events/serialization/SessionWorkspaceBoundEventSerializationTest.kt b/core/events/src/test/kotlin/com/correx/core/events/serialization/SessionWorkspaceBoundEventSerializationTest.kt new file mode 100644 index 00000000..9e3f5305 --- /dev/null +++ b/core/events/src/test/kotlin/com/correx/core/events/serialization/SessionWorkspaceBoundEventSerializationTest.kt @@ -0,0 +1,38 @@ +package com.correx.core.events.serialization + +import com.correx.core.events.events.EventPayload +import com.correx.core.events.events.SessionWorkspaceBoundEvent +import com.correx.core.events.types.SessionId +import kotlin.test.Test +import kotlin.test.assertEquals +import kotlin.test.assertTrue + +class SessionWorkspaceBoundEventSerializationTest { + + private val sample = SessionWorkspaceBoundEvent( + sessionId = SessionId("sess-1"), + workspaceRoot = "/home/user/project", + allowedPaths = listOf("/home/user/project", "/home/user/project/src"), + ) + + @Test + fun `round-trips as polymorphic EventPayload`() { + val encoded = eventJson.encodeToString(EventPayload.serializer(), sample) + assertTrue(encoded.contains("\"type\":\"SessionWorkspaceBound\""), "SerialName must be present: $encoded") + val decoded = eventJson.decodeFromString(EventPayload.serializer(), encoded) + assertEquals(sample, decoded) + } + + @Test + fun `decodes hand-written SessionWorkspaceBound JSON`() { + val json = """ + {"type":"SessionWorkspaceBound","sessionId":"s-42","workspaceRoot":"/ws","allowedPaths":["/ws","/ws/src"]} + """.trimIndent() + val decoded = eventJson.decodeFromString(EventPayload.serializer(), json) + assertTrue(decoded is SessionWorkspaceBoundEvent) + decoded as SessionWorkspaceBoundEvent + assertEquals("s-42", decoded.sessionId.value) + assertEquals("/ws", decoded.workspaceRoot) + assertEquals(listOf("/ws", "/ws/src"), decoded.allowedPaths) + } +} diff --git a/core/kernel/src/main/kotlin/com/correx/core/kernel/orchestration/OrchestrationConfig.kt b/core/kernel/src/main/kotlin/com/correx/core/kernel/orchestration/OrchestrationConfig.kt index 0fe83203..53c342a9 100644 --- a/core/kernel/src/main/kotlin/com/correx/core/kernel/orchestration/OrchestrationConfig.kt +++ b/core/kernel/src/main/kotlin/com/correx/core/kernel/orchestration/OrchestrationConfig.kt @@ -10,4 +10,5 @@ data class OrchestrationConfig( val stageTimeoutMs: Long = 60_000L, val sandboxRoot: Path = Path.of(System.getProperty("java.io.tmpdir"), "correx-sandbox"), val defaultSystemPromptPath: String? = null, + val workspace: WorkspaceContext? = null, ) diff --git a/core/kernel/src/main/kotlin/com/correx/core/kernel/orchestration/WorkspaceContext.kt b/core/kernel/src/main/kotlin/com/correx/core/kernel/orchestration/WorkspaceContext.kt new file mode 100644 index 00000000..e75f040a --- /dev/null +++ b/core/kernel/src/main/kotlin/com/correx/core/kernel/orchestration/WorkspaceContext.kt @@ -0,0 +1,10 @@ +package com.correx.core.kernel.orchestration + +import java.nio.file.Path + +data class WorkspaceContext( + val workspaceRoot: Path, + val workingDir: Path, + val allowedPaths: Set, + val privilegedLocations: List = emptyList(), +)