memory-vector: LlmMemoryReviewer + SkillStore/SkillSaveTool/SkillDeleteTool

Phase 3 (Hermes-style self-improvement) and Phase 5.2 (LLM-driven review):

* :skills — SkillStore interface + DiskSkillStore (upsert/remove, file<->catalog sync)
* :skills — SkillParser.serialize for write-back path
* :standalone — SkillSaveTool/SkillDeleteTool + SkillToolsFactory
* :standalone — LlmMemoryReviewer: one-shot LiteLlm review via structured-output
  JSON prompt ({toSave:[...], toDelete:[...]}); reuses MemorySystem store
* :standalone — ReviewDecisionParser (lenient, handles json fences, missing
  arrays, malformed numbers)
* :standalone — ReviewPrompts (Russian system+user prompts, fact categories)
* :standalone/Main — wires LlmMemoryReviewer instead of KeywordMdReviewer when
  LLM is available, falls back to keyword for off/md-only mode
* :client — send(content, context) overload + SendPayload wrapper
* :proto — ExperimentalNativeApi opt-in for MessageContextTest (native targets)

Bug fixes:
* SkillTools.kt: error() shadowed kotlin.error(); renamed to Nothing
* ChatConversation: .map { when(...); error() } → .mapNotNull { when ... else -> null }
  (Kotlin type inference of LUB LiteMessage | Nothing failed across when-expr)

Tests: 284 total green (memory-vector: 17, standalone: 149).

Dropped: IRC-QUESTIONS.md (irc-server design rejected — user decision 2026-09-14).
This commit is contained in:
2026-09-15 04:08:46 +03:00
parent 65365da89c
commit 227d14b7e4
18 changed files with 1070 additions and 50 deletions
@@ -13,10 +13,12 @@ import io.ktor.http.HttpStatusCode
import io.ktor.http.contentType
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.flow
import kotlinx.serialization.Serializable
import pw.binom.agentik.proto.Content
import pw.binom.agentik.proto.Conversation
import pw.binom.agentik.proto.Event
import pw.binom.agentik.proto.Message
import pw.binom.agentik.proto.MessageContext
import kotlin.time.Instant
/**
@@ -58,10 +60,10 @@ internal class ConversationClient(
snapshot = updated
}
override suspend fun send(content: List<Content>) {
override suspend fun send(content: List<Content>, context: MessageContext?) {
httpClient.post("$convUrl/messages") {
contentType(ContentType.Application.Json)
setBody(content)
setBody(SendPayload(content, context))
}
}
@@ -92,3 +94,9 @@ internal class ConversationClient(
// Agent.deleteConversation(id). См. [Conversation.close] KDoc.
}
}
@Serializable
private data class SendPayload(
val content: List<Content>,
val context: MessageContext? = null,
)