fix(kernel+infra): tool approval gate and executor cleanup

- SessionOrchestrator: wire T2/T3/T4 approval gate before tool execution;
  emit OrchestrationPausedEvent/ApprovalRequestedEvent, await decision,
  return rejection as ERROR context entry so LLM sees the denial;
  propagate tool ERROR entries as StageExecutionResult.Failure
- SandboxedToolExecutor: remove dead code and simplify
- InfrastructureModule: minor wiring cleanup
- LlamaCppInferenceProvider / build.gradle: related build fixes
This commit is contained in:
2026-05-19 00:05:08 +04:00
parent f32d400138
commit c0efa0ef03
5 changed files with 86 additions and 90 deletions
@@ -17,8 +17,6 @@ dependencies {
implementation project(':core:context')
implementation project(':infrastructure:inference:commons')
implementation("com.fasterxml.jackson.core:jackson-databind:2.17.0")
implementation("com.fasterxml.jackson.module:jackson-module-kotlin:2.17.0")
implementation "io.ktor:ktor-client-core:$ktor_version"
implementation "io.ktor:ktor-client-cio:$ktor_version"
implementation "io.ktor:ktor-client-content-negotiation:$ktor_version"
@@ -25,7 +25,9 @@ import io.ktor.client.request.setBody
import io.ktor.http.ContentType
import io.ktor.http.contentType
import io.ktor.serialization.kotlinx.json.json
import kotlinx.serialization.encodeToString
import kotlinx.serialization.json.Json
import org.slf4j.LoggerFactory
private const val DEFAULT_REQUEST_TIMEOUT_MS = 60_000L
@@ -42,7 +44,23 @@ private fun defaultHttpClient(): HttpClient = HttpClient(CIO) {
install(HttpTimeout) {
requestTimeoutMillis = DEFAULT_REQUEST_TIMEOUT_MS
}
install(ContentNegotiation) {
json(
Json {
ignoreUnknownKeys = true
explicitNulls = false
encodeDefaults = true
},
)
}
}
private val json = Json {
ignoreUnknownKeys = true
explicitNulls = false
encodeDefaults = true
}
private val log = LoggerFactory.getLogger(LlamaCppInferenceProvider::class.java)
@Suppress("TooGenericExceptionCaught", "MagicNumber")
class LlamaCppInferenceProvider(
@@ -80,12 +98,17 @@ class LlamaCppInferenceProvider(
tools = request.tools.takeIf { it.isNotEmpty() },
)
val encoded = json.encodeToString(body)
log.debug("sending request to llm: {}", encoded)
val response = httpClient.post("$baseUrl/v1/chat/completions") {
contentType(ContentType.Application.Json)
accept(ContentType.Application.Json)
setBody(body)
setBody(encoded)
}.body<ChatCompletionResponse>()
log.debug("got response from llm: {}", response)
val message = response.choices.first().message
val finishReason = when (response.choices.first().finishReason.lowercase()) {
"length" -> FinishReason.Length