feature(event-sourcing): baseline for the architecture review fixes.

- fixed the tool overlay in tui.
- fixed tool calling - added schemas.
- getting all sessionIds via event store.
- instead of sending all events on the replay - there's a new way for replaying.
- session snapshot is now a thing getting sent for previously created sessions.
- added logging to important parts.
- revert workflowId from WorkflowCompletedEvent.
This commit is contained in:
2026-05-24 18:50:00 +04:00
parent f827685ed0
commit fc7b879891
43 changed files with 525 additions and 211 deletions
@@ -12,8 +12,13 @@ import kotlinx.coroutines.TimeoutCancellationException
import kotlinx.coroutines.async
import kotlinx.coroutines.withContext
import kotlinx.coroutines.withTimeout
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 java.nio.file.Path
class ShellTool(
@@ -23,18 +28,36 @@ class ShellTool(
) : Tool, ToolExecutor {
override val name: String = "shell"
override val description: String = "Execute a shell command"
override val parametersSchema: JsonObject = buildJsonObject {}
override val parametersSchema: JsonObject = buildJsonObject {
put("type", "object")
putJsonObject("properties") {
putJsonObject("argv") {
put("type", "array")
putJsonObject("items") { put("type", "string") }
put("description", "Command and arguments as a list. Example: [\"bash\", \"script.sh\"]")
}
}
put("required", buildJsonArray { add(JsonPrimitive("argv")) })
}
override val tier: Tier = Tier.T2
override val requiredCapabilities: Set<ToolCapability> =
setOf(ToolCapability.SHELL_EXEC, ToolCapability.PROCESS_SPAWN)
override fun validateRequest(request: ToolRequest): ValidationResult {
val argv = (request.parameters["argv"] as? List<*>)?.filterIsInstance<String>()
val argv = when (val raw = request.parameters["argv"]) {
is List<*> -> raw.filterIsInstance<String>()
is String -> runCatching {
Json.decodeFromString<List<String>>(raw)
}.getOrElse { emptyList() }
else -> emptyList()
}
return when {
argv.isNullOrEmpty() ->
argv.isEmpty() ->
ValidationResult.Invalid("Missing or empty 'argv' parameter. Expected List<String>.")
allowedExecutables.isNotEmpty() && argv[0] !in allowedExecutables ->
ValidationResult.Invalid("Executable '${argv[0]}' is not in the allowed list.")
else -> ValidationResult.Valid
}
}
@@ -42,7 +65,13 @@ class ShellTool(
override suspend fun execute(request: ToolRequest): ToolResult = withContext(Dispatchers.IO) {
validateRequest(request).run {
takeIf { this is ValidationResult.Valid }?.let {
val argv = (request.parameters["argv"] as? List<*>)?.filterIsInstance<String>()
val argv = when (val raw = request.parameters["argv"]) {
is List<*> -> raw.filterIsInstance<String>()
is String -> runCatching {
Json.decodeFromString<List<String>>(raw)
}.getOrElse { emptyList() }
else -> emptyList()
}
val process = ProcessBuilder(argv).apply {
workingDir?.let { directory(it.toFile()) }
}.start()