chore(audit): tool path resolution, config file, dead code removal, static analysis cleanup
Tool fixes: - FileWriteTool, FileEditTool: resolve relative paths against workingDir instead of JVM cwd - FileReadTool: remove allowedPaths restriction — reads are unrestricted - ShellTool: add workingDir for ProcessBuilder, remove environment().clear(), fail-open when allowedExecutables empty Config: - Add [tools] section to config.toml: sandbox_root, working_dir, shell_allowed_executables, default_system_prompt_path - Three-level resolution: env var > config file > hardcoded default - OrchestrationConfig sandboxRoot and defaultSystemPromptPath now sourced from CorrexConfig - Single defaultOrchestrationConfig in ServerModule, shared by SessionRoutes and GlobalStreamHandler Dead code: - Delete EventTypes.kt — orphaned string constants duplicating serialization annotations Static analysis: - Remove 10 unused imports across 9 files - Remove redundant return in ReplayOrchestrator - Remove redundant suspend from ApprovalCoordinator.handleResponse - Fix unreachable InputMode.None branch in InputBar - Enable full detekt rule sets in detekt.yml
This commit is contained in:
@@ -9,6 +9,7 @@ application {
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation project(':core:config')
|
||||
implementation project(':core:events')
|
||||
implementation project(':core:approvals')
|
||||
implementation project(':core:sessions')
|
||||
|
||||
@@ -20,22 +20,28 @@ import com.correx.core.sessions.DefaultSessionReducer
|
||||
import com.correx.core.sessions.DefaultSessionRepository
|
||||
import com.correx.core.sessions.SessionProjector
|
||||
import com.correx.core.sessions.projections.replay.DefaultEventReplayer
|
||||
import com.correx.core.transitions.evaluation.PromptResolver
|
||||
import com.correx.core.transitions.resolution.DefaultTransitionResolver
|
||||
import com.correx.core.validation.artifact.ArtifactPayloadValidator
|
||||
import com.correx.core.validation.pipeline.ValidationPipeline
|
||||
import com.correx.apps.server.logging.LoggingEventStore
|
||||
import com.correx.core.artifactstore.ArtifactStore
|
||||
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.core.config.ConfigLoader
|
||||
import com.correx.core.kernel.orchestration.OrchestrationConfig
|
||||
import com.correx.infrastructure.tools.FileEditConfig
|
||||
import com.correx.infrastructure.tools.FileWriteConfig
|
||||
import com.correx.infrastructure.tools.ShellConfig
|
||||
import com.correx.infrastructure.tools.ToolConfig
|
||||
import com.correx.core.events.EventDispatcher
|
||||
import com.correx.core.events.stores.EventStore
|
||||
import com.correx.infrastructure.inference.llama.cpp.LlamaCppInferenceProvider
|
||||
import io.ktor.server.engine.embeddedServer
|
||||
import io.ktor.server.netty.Netty
|
||||
import org.slf4j.LoggerFactory
|
||||
import java.nio.file.Path
|
||||
import java.nio.file.Paths
|
||||
|
||||
private val log = LoggerFactory.getLogger("com.correx.apps.server.Main")
|
||||
@@ -46,8 +52,18 @@ fun main() {
|
||||
val infraRegistry = InfrastructureModule.createProviderRegistry(listOf(buildLlamaProvider()))
|
||||
val repositories = buildRepositories(eventStore)
|
||||
val approvalEngine = DefaultApprovalEngine()
|
||||
val sandboxRoot = Paths.get(System.getProperty("user.home"), ".config", "correx", "sandbox")
|
||||
val toolRegistry = InfrastructureModule.createToolRegistry(buildToolConfig(artifactStore, sandboxRoot))
|
||||
val correxConfig = ConfigLoader.load()
|
||||
val toolsConfig = correxConfig.tools
|
||||
val sandboxRoot = System.getenv("CORREX_SANDBOX_ROOT")
|
||||
?.let { Path.of(it) }
|
||||
?: toolsConfig.sandboxRoot.takeIf { it.isNotEmpty() }?.let { Path.of(it) }
|
||||
?: Paths.get(System.getProperty("user.home"), ".config", "correx", "sandbox")
|
||||
val workingDir = System.getenv("CORREX_WORKING_DIR")
|
||||
?.let { Path.of(it) }
|
||||
?: toolsConfig.workingDir.takeIf { it.isNotEmpty() }?.let { Path.of(it) }
|
||||
?: Path.of("").toAbsolutePath()
|
||||
val shellAllowedExecutables = toolsConfig.shellAllowedExecutables.toSet()
|
||||
val toolRegistry = InfrastructureModule.createToolRegistry(buildToolConfig(artifactStore, sandboxRoot, workingDir, shellAllowedExecutables))
|
||||
val toolExecutor = InfrastructureModule.createToolExecutor(
|
||||
registry = toolRegistry,
|
||||
eventDispatcher = EventDispatcher(eventStore),
|
||||
@@ -70,6 +86,10 @@ fun main() {
|
||||
retryCoordinator = DefaultRetryCoordinator(eventStore),
|
||||
artifactStore = artifactStore,
|
||||
)
|
||||
val defaultOrchestrationConfig = OrchestrationConfig(
|
||||
sandboxRoot = sandboxRoot,
|
||||
defaultSystemPromptPath = toolsConfig.defaultSystemPromptPath,
|
||||
)
|
||||
val module = ServerModule(
|
||||
orchestrator = orchestrator,
|
||||
eventStore = eventStore,
|
||||
@@ -77,6 +97,7 @@ fun main() {
|
||||
sessionRepository = repositories.sessionRepository,
|
||||
workflowRegistry = FileSystemWorkflowRegistry(InfrastructureModule.createWorkflowLoader()),
|
||||
providerRegistry = infraRegistry.asServerRegistry(),
|
||||
defaultOrchestrationConfig = defaultOrchestrationConfig,
|
||||
)
|
||||
val modelId = System.getenv("CORREX_MODEL_ID") ?: "default"
|
||||
val modelPath = System.getenv("CORREX_MODEL_PATH") ?: "(not set)"
|
||||
@@ -87,6 +108,10 @@ fun main() {
|
||||
log.info(" model path : {}", modelPath)
|
||||
log.info(" llama url : {}", llamaUrl)
|
||||
log.info(" sandbox root : {}", sandboxRoot)
|
||||
log.info(" working dir : {}", workingDir)
|
||||
if (shellAllowedExecutables.isEmpty()) {
|
||||
log.warn(" shell tool : no executable allowlist configured — all executables allowed")
|
||||
}
|
||||
log.info(" event store : {}", eventStore::class.simpleName)
|
||||
log.info(" artifact store: {}", artifactStore::class.simpleName)
|
||||
log.info(" tools : {}", toolRegistry.all().map { it.name })
|
||||
@@ -96,15 +121,15 @@ fun main() {
|
||||
embeddedServer(Netty, port = 8080) { configureServer(module) }.start(wait = true)
|
||||
}
|
||||
|
||||
private fun buildLlamaProvider() = InfrastructureModule.createLlamaCppProvider(
|
||||
private fun buildLlamaProvider(): LlamaCppInferenceProvider = InfrastructureModule.createLlamaCppProvider(
|
||||
modelId = System.getenv("CORREX_MODEL_ID") ?: "default",
|
||||
modelPath = System.getenv("CORREX_MODEL_PATH") ?: "",
|
||||
baseUrl = System.getenv("CORREX_LLAMA_URL") ?: "http://127.0.0.1:10000",
|
||||
)
|
||||
|
||||
private fun buildRepositories(
|
||||
eventStore: com.correx.core.events.stores.EventStore,
|
||||
) = OrchestratorRepositories(
|
||||
eventStore: EventStore,
|
||||
): OrchestratorRepositories = OrchestratorRepositories(
|
||||
eventStore = eventStore,
|
||||
inferenceRepository = InferenceRepository(DefaultEventReplayer(eventStore, InferenceProjector())),
|
||||
orchestrationRepository = OrchestrationRepository(
|
||||
@@ -117,15 +142,28 @@ private fun buildRepositories(
|
||||
)
|
||||
|
||||
private fun buildToolConfig(
|
||||
artifactStore: com.correx.core.artifactstore.ArtifactStore,
|
||||
sandboxRoot: java.nio.file.Path,
|
||||
) = ToolConfig(
|
||||
shell = ShellConfig(enabled = true),
|
||||
artifactStore: ArtifactStore,
|
||||
sandboxRoot: Path,
|
||||
workingDir: Path,
|
||||
shellAllowedExecutables: Set<String>,
|
||||
): ToolConfig = ToolConfig(
|
||||
shell = ShellConfig(
|
||||
enabled = true,
|
||||
allowedExecutables = shellAllowedExecutables,
|
||||
workingDir = workingDir,
|
||||
),
|
||||
fileWrite = FileWriteConfig(
|
||||
allowedPaths = setOf(sandboxRoot, workingDir),
|
||||
enabled = true,
|
||||
artifactStore = artifactStore,
|
||||
materializingWriter = DefaultMaterializingArtifactWriter(),
|
||||
sandboxRoot = sandboxRoot,
|
||||
workingDir = workingDir,
|
||||
),
|
||||
fileEdit = FileEditConfig(
|
||||
enabled = true,
|
||||
allowedPaths = setOf(sandboxRoot, workingDir),
|
||||
workingDir = workingDir,
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@ import com.correx.apps.server.registry.WorkflowRegistry
|
||||
import com.correx.core.artifactstore.ArtifactStore
|
||||
import com.correx.core.events.stores.EventStore
|
||||
import com.correx.core.kernel.orchestration.DefaultSessionOrchestrator
|
||||
import com.correx.core.kernel.orchestration.OrchestrationConfig
|
||||
import com.correx.core.sessions.DefaultSessionRepository
|
||||
|
||||
data class ServerModule(
|
||||
@@ -14,4 +15,5 @@ data class ServerModule(
|
||||
val sessionRepository: DefaultSessionRepository,
|
||||
val workflowRegistry: WorkflowRegistry,
|
||||
val providerRegistry: ProviderRegistry,
|
||||
val defaultOrchestrationConfig: OrchestrationConfig,
|
||||
)
|
||||
|
||||
@@ -62,7 +62,7 @@ class ApprovalCoordinator(
|
||||
scheduleTimeout(event.requestId, event.sessionId, event.stageId, event.tier)
|
||||
}
|
||||
|
||||
suspend fun handleResponse(msg: ClientMessage.ApprovalResponse, sessionId: SessionId): ServerMessage? {
|
||||
fun handleResponse(msg: ClientMessage.ApprovalResponse, sessionId: SessionId): ServerMessage? {
|
||||
if (resolved.putIfAbsent(msg.requestId, true) != null) {
|
||||
return ServerMessage.ProtocolError("Approval request ${msg.requestId.value} already resolved")
|
||||
}
|
||||
|
||||
@@ -2,7 +2,6 @@ package com.correx.apps.server.protocol
|
||||
|
||||
import kotlinx.serialization.json.Json
|
||||
import kotlinx.serialization.encodeToString
|
||||
import kotlinx.serialization.decodeFromString
|
||||
|
||||
class ProtocolException(message: String, cause: Throwable? = null) : RuntimeException(message, cause)
|
||||
|
||||
|
||||
@@ -10,7 +10,6 @@ import com.correx.core.events.types.ApprovalRequestId
|
||||
import com.correx.core.events.types.SessionId
|
||||
import com.correx.core.utils.TypeId
|
||||
import io.ktor.http.HttpStatusCode
|
||||
import io.ktor.server.application.call
|
||||
import io.ktor.server.request.receive
|
||||
import io.ktor.server.response.respond
|
||||
import io.ktor.server.routing.Route
|
||||
|
||||
@@ -3,7 +3,6 @@ package com.correx.apps.server.routes
|
||||
import com.correx.apps.server.ServerModule
|
||||
import com.correx.apps.server.protocol.ProviderHealthDto
|
||||
import com.correx.core.inference.ProviderHealth
|
||||
import io.ktor.server.application.call
|
||||
import io.ktor.server.response.respond
|
||||
import io.ktor.server.routing.Route
|
||||
import io.ktor.server.routing.get
|
||||
|
||||
@@ -8,11 +8,8 @@ import com.correx.core.events.events.NewEvent
|
||||
import com.correx.core.events.events.WorkflowFailedEvent
|
||||
import com.correx.core.events.types.EventId
|
||||
import com.correx.core.events.types.SessionId
|
||||
import com.correx.core.kernel.orchestration.OrchestrationConfig
|
||||
import com.correx.core.utils.TypeId
|
||||
import io.ktor.http.HttpStatusCode
|
||||
import io.ktor.server.application.call
|
||||
import io.ktor.server.application.application
|
||||
import org.slf4j.LoggerFactory
|
||||
import io.ktor.server.request.receive
|
||||
import io.ktor.server.response.respond
|
||||
@@ -67,9 +64,7 @@ private fun Route.startSessionRoute(module: ServerModule) {
|
||||
val graph = module.workflowRegistry.find(body.workflowId)
|
||||
?: return@post call.respond(HttpStatusCode.BadRequest, "Unknown workflowId: ${body.workflowId}")
|
||||
val sessionId: SessionId = TypeId(UUID.randomUUID().toString())
|
||||
val config = OrchestrationConfig(
|
||||
defaultSystemPromptPath = "~/.config/correx/prompts/default_system.md",
|
||||
)
|
||||
val config = module.defaultOrchestrationConfig
|
||||
call.application.launch {
|
||||
runCatching { module.orchestrator.run(sessionId, graph, config) }
|
||||
.onFailure { ex ->
|
||||
|
||||
@@ -2,7 +2,6 @@ package com.correx.apps.server.routes
|
||||
|
||||
import com.correx.apps.server.ServerModule
|
||||
import com.correx.apps.server.registry.WorkflowSummary
|
||||
import io.ktor.server.application.call
|
||||
import io.ktor.server.response.respond
|
||||
import io.ktor.server.routing.Route
|
||||
import io.ktor.server.routing.get
|
||||
|
||||
@@ -12,7 +12,6 @@ import com.correx.core.events.events.WorkflowFailedEvent
|
||||
import com.correx.core.events.types.EventId
|
||||
import com.correx.core.events.types.SessionId
|
||||
import com.correx.core.inference.ProviderHealth
|
||||
import com.correx.core.kernel.orchestration.OrchestrationConfig
|
||||
import com.correx.core.utils.TypeId
|
||||
import io.ktor.server.websocket.DefaultWebSocketServerSession
|
||||
import io.ktor.websocket.Frame
|
||||
@@ -104,7 +103,7 @@ class GlobalStreamHandler(private val module: ServerModule) {
|
||||
is ClientMessage.CancelSession -> module.orchestrator.cancel(msg.sessionId)
|
||||
is ClientMessage.ResumeSession -> session.send(encodeError("ResumeSession not supported"))
|
||||
is ClientMessage.ApprovalResponse ->
|
||||
session.send(encodeError("ApprovalResponse not supported on global stream"))
|
||||
session.send(encodeError("ApprovalResponse must be sent to /sessions/{id}/stream"))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -127,10 +126,7 @@ class GlobalStreamHandler(private val module: ServerModule) {
|
||||
val sessionId: SessionId = TypeId(UUID.randomUUID().toString())
|
||||
log.info("starting session={} workflow={}", sessionId.value, msg.workflowId)
|
||||
session.launch {
|
||||
val config = OrchestrationConfig(
|
||||
defaultSystemPromptPath = "~/.config/correx/prompts/default_system.md",
|
||||
)
|
||||
runCatching { module.orchestrator.run(sessionId, graph, config) }
|
||||
runCatching { module.orchestrator.run(sessionId, graph, module.defaultOrchestrationConfig) }
|
||||
.onFailure { ex ->
|
||||
log.error("run failed for session={}: {}", sessionId.value, ex.message, ex)
|
||||
module.eventStore.append(
|
||||
|
||||
Reference in New Issue
Block a user