feat(workspace): WorkspaceContext + SessionWorkspaceBoundEvent foundation
Add a WorkspaceContext value type and a nullable OrchestrationConfig.workspace, plus SessionWorkspaceBoundEvent registered in eventModule. Purely additive; no behavior change when workspace is null. (Axis 2 Phase A, tasks 1-2.)
This commit is contained in:
@@ -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<String>,
|
||||
) : EventPayload
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
+38
@@ -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)
|
||||
}
|
||||
}
|
||||
+1
@@ -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,
|
||||
)
|
||||
|
||||
@@ -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<Path>,
|
||||
val privilegedLocations: List<Path> = emptyList(),
|
||||
)
|
||||
Reference in New Issue
Block a user