refactor: retire sandbox dual-write; file tools write in-place into workspace
This commit is contained in:
@@ -44,7 +44,6 @@ import com.correx.core.toolintent.rules.ExecInterpreterRule
|
||||
import com.correx.core.toolintent.rules.NetworkHostRule
|
||||
import com.correx.core.toolintent.rules.PathContainmentRule
|
||||
import com.correx.infrastructure.InfrastructureModule
|
||||
import com.correx.infrastructure.artifactscas.DefaultMaterializingArtifactWriter
|
||||
import com.correx.infrastructure.inference.DefaultProviderRegistry
|
||||
import com.correx.infrastructure.inference.FirstAvailableRoutingStrategy
|
||||
import com.correx.infrastructure.inference.llama.cpp.LlamaCppInferenceProvider
|
||||
@@ -92,10 +91,13 @@ fun main() {
|
||||
?: toolsConfig.workingDir.takeIf { it.isNotEmpty() }?.let { Path.of(it) }
|
||||
?: Path.of("").toAbsolutePath()
|
||||
val shellAllowedExecutables = toolsConfig.shellAllowedExecutables.toSet()
|
||||
val workspaceRoot = System.getenv("CORREX_WORKSPACE_ROOT")
|
||||
?.let { Path.of(it) }
|
||||
?: toolsConfig.workspaceRoot.takeIf { it.isNotEmpty() }?.let { Path.of(it) }
|
||||
?: workingDir
|
||||
val toolRegistry = InfrastructureModule.createToolRegistry(
|
||||
buildToolConfig(
|
||||
artifactStore,
|
||||
sandboxRoot,
|
||||
workspaceRoot,
|
||||
workingDir,
|
||||
shellAllowedExecutables,
|
||||
toolsConfig,
|
||||
@@ -110,10 +112,6 @@ fun main() {
|
||||
|
||||
logToolInfo(sandboxRoot, workingDir, shellAllowedExecutables, toolRegistry)
|
||||
|
||||
val workspaceRoot = System.getenv("CORREX_WORKSPACE_ROOT")
|
||||
?.let { Path.of(it) }
|
||||
?: toolsConfig.workspaceRoot.takeIf { it.isNotEmpty() }?.let { Path.of(it) }
|
||||
?: workingDir
|
||||
val privilegedLocations = toolsConfig.privilegedLocations.map { Path.of(it) }
|
||||
val workspacePolicy = WorkspacePolicy(workspaceRoot, privilegedLocations)
|
||||
val toolCallAssessor = ToolCallAssessor(
|
||||
@@ -297,13 +295,18 @@ private fun buildRepositories(
|
||||
approvalRepository = InfrastructureModule.createApprovalRepository(eventStore),
|
||||
)
|
||||
|
||||
// File tools write IN PLACE into the workspace (cwd / allow-listed dirs). The old
|
||||
// sandboxRoot dual-write is retired; reversibility is event-sourced (FileWrittenEvent +
|
||||
// CAS pre/post images, captured by SandboxedToolExecutor). sandboxRoot survives only as
|
||||
// the executor's scratch workDir, never as a write destination.
|
||||
private fun buildToolConfig(
|
||||
artifactStore: ArtifactStore,
|
||||
sandboxRoot: Path,
|
||||
workspaceRoot: Path,
|
||||
workingDir: Path,
|
||||
shellAllowedExecutables: Set<String>,
|
||||
toolsConfig: com.correx.core.config.ToolsConfig,
|
||||
): ToolConfig = ToolConfig(
|
||||
): ToolConfig {
|
||||
val allowed = setOf(workspaceRoot, workingDir)
|
||||
return ToolConfig(
|
||||
shell = ShellConfig(
|
||||
enabled = toolsConfig.shellEnabled,
|
||||
allowedExecutables = shellAllowedExecutables,
|
||||
@@ -311,22 +314,20 @@ private fun buildToolConfig(
|
||||
),
|
||||
fileRead = FileReadConfig(
|
||||
enabled = toolsConfig.fileReadEnabled,
|
||||
allowedPaths = setOf(sandboxRoot, workingDir),
|
||||
allowedPaths = allowed,
|
||||
),
|
||||
fileWrite = FileWriteConfig(
|
||||
allowedPaths = setOf(sandboxRoot, workingDir),
|
||||
allowedPaths = allowed,
|
||||
enabled = toolsConfig.fileWriteEnabled,
|
||||
artifactStore = artifactStore,
|
||||
materializingWriter = DefaultMaterializingArtifactWriter(),
|
||||
sandboxRoot = sandboxRoot,
|
||||
workingDir = workingDir,
|
||||
),
|
||||
fileEdit = FileEditConfig(
|
||||
enabled = toolsConfig.fileEditEnabled,
|
||||
allowedPaths = setOf(sandboxRoot, workingDir),
|
||||
allowedPaths = allowed,
|
||||
workingDir = workingDir,
|
||||
),
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
private fun DefaultProviderRegistry.asServerRegistry(): ProviderRegistry = object : ProviderRegistry {
|
||||
override fun listAll() = this@asServerRegistry.listAll()
|
||||
|
||||
-36
@@ -1,11 +1,6 @@
|
||||
package com.correx.infrastructure.tools.filesystem
|
||||
|
||||
import com.correx.core.approvals.Tier
|
||||
import com.correx.core.artifacts.MaterializationResult
|
||||
import com.correx.core.artifacts.MaterializingArtifactWriter
|
||||
import com.correx.core.artifacts.kind.FileWrittenArtifact
|
||||
import com.correx.core.artifacts.kind.FileWrittenPayload
|
||||
import com.correx.core.artifactstore.ArtifactStore
|
||||
import com.correx.core.events.events.ToolRequest
|
||||
import com.correx.core.events.types.ToolInvocationId
|
||||
import com.correx.core.tools.contract.FileAffectingTool
|
||||
@@ -17,15 +12,12 @@ import com.correx.core.tools.contract.ValidationResult
|
||||
import kotlinx.coroutines.CancellationException
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.withContext
|
||||
import kotlinx.serialization.json.Json
|
||||
import kotlinx.serialization.json.JsonObject
|
||||
import kotlinx.serialization.json.JsonPrimitive
|
||||
import kotlinx.serialization.json.buildJsonArray
|
||||
import kotlinx.serialization.json.buildJsonObject
|
||||
import kotlinx.serialization.json.put
|
||||
import kotlinx.serialization.json.putJsonObject
|
||||
import org.slf4j.Logger
|
||||
import org.slf4j.LoggerFactory
|
||||
import java.io.IOException
|
||||
import java.nio.file.Files
|
||||
import java.nio.file.InvalidPathException
|
||||
@@ -34,16 +26,9 @@ import java.nio.file.Paths
|
||||
|
||||
class FileWriteTool(
|
||||
allowedPaths: Set<Path> = emptySet(),
|
||||
private val artifactStore: ArtifactStore? = null,
|
||||
private val materializingWriter: MaterializingArtifactWriter? = null,
|
||||
private val sandboxRoot: Path? = null,
|
||||
private val workingDir: Path? = null,
|
||||
) : Tool, FileAffectingTool, ToolExecutor {
|
||||
|
||||
private companion object {
|
||||
val log: Logger = LoggerFactory.getLogger(FileWriteTool::class.java)
|
||||
}
|
||||
|
||||
private val normalizedAllowedPaths: Set<Path> = allowedPaths.map { it.normalize().toAbsolutePath() }.toSet()
|
||||
|
||||
override val name: String = "file_write"
|
||||
@@ -174,7 +159,6 @@ class FileWriteTool(
|
||||
withContext(Dispatchers.IO) {
|
||||
AtomicFileWriter.write(path, content.toByteArray(Charsets.UTF_8))
|
||||
}
|
||||
storeArtifact(pathString, content, request.parameters["mode"] as? String ?: "0644")
|
||||
ToolResult.Success(
|
||||
invocationId = request.invocationId,
|
||||
output = "File written successfully to $pathString",
|
||||
@@ -206,26 +190,6 @@ class FileWriteTool(
|
||||
)
|
||||
}
|
||||
|
||||
@Suppress("ReturnCount")
|
||||
private suspend fun storeArtifact(pathString: String, content: String, mode: String) {
|
||||
val writer = materializingWriter ?: return
|
||||
val store = artifactStore ?: return
|
||||
val root = sandboxRoot ?: return
|
||||
val payload = FileWrittenPayload(path = pathString, content = content, mode = mode)
|
||||
when (val result = writer.materialize(payload, root)) {
|
||||
is MaterializationResult.Success -> {
|
||||
val canonicalBytes = Json.encodeToString(
|
||||
FileWrittenArtifact.serializer(),
|
||||
result.artifact,
|
||||
).toByteArray(Charsets.UTF_8)
|
||||
store.put(canonicalBytes)
|
||||
}
|
||||
|
||||
is MaterializationResult.Failure ->
|
||||
log.warn("[FileWriteTool] artifact materialization failed for {}: {}", pathString, result.reason)
|
||||
}
|
||||
}
|
||||
|
||||
private fun handleExecutionException(
|
||||
e: Throwable,
|
||||
invocationId: ToolInvocationId,
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
package com.correx.infrastructure.tools
|
||||
|
||||
import com.correx.core.artifacts.MaterializingArtifactWriter
|
||||
import com.correx.core.artifactstore.ArtifactStore
|
||||
import com.correx.core.tools.contract.Tool
|
||||
import com.correx.infrastructure.tools.filesystem.FileEditTool
|
||||
import com.correx.infrastructure.tools.filesystem.FileReadTool
|
||||
@@ -20,9 +18,6 @@ data class FileReadConfig(val enabled: Boolean = false, val allowedPaths: Set<Pa
|
||||
data class FileWriteConfig(
|
||||
val enabled: Boolean = false,
|
||||
val allowedPaths: Set<Path> = emptySet(),
|
||||
val artifactStore: ArtifactStore? = null,
|
||||
val materializingWriter: MaterializingArtifactWriter? = null,
|
||||
val sandboxRoot: Path? = null,
|
||||
val workingDir: Path? = null,
|
||||
)
|
||||
|
||||
@@ -61,9 +56,6 @@ fun ToolConfig.buildTools(): List<Tool> = buildList {
|
||||
add(
|
||||
FileWriteTool(
|
||||
allowedPaths = fileWrite.allowedPaths,
|
||||
artifactStore = fileWrite.artifactStore,
|
||||
materializingWriter = fileWrite.materializingWriter,
|
||||
sandboxRoot = fileWrite.sandboxRoot,
|
||||
workingDir = fileWrite.workingDir,
|
||||
),
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user