remove :message-store-api and migrate functionality to :journal-api and :reflection-api
ci / JVM build + tests (push) Failing after 59s

- Removed `:message-store-api` module and associated classes (ConversationStore, ReflectionStore, Ids, etc.).
- Migrated reusable components to `:journal-api` (conversation-related) and `:reflection-api` (reflection-related).
- Updated imports and module dependencies across all projects to reflect new structure.
- Adjusted build scripts and tests for compatibility with the new APIs.
This commit is contained in:
2026-09-21 03:24:20 +03:00
parent 2d6cf89c52
commit 0d1be42919
37 changed files with 60 additions and 200 deletions
+2 -1
View File
@@ -21,7 +21,8 @@ kotlin {
sourceSets {
commonMain.dependencies {
api(project(":message-store-api"))
api(project(":journal-api"))
api(project(":reflection-api"))
api(project(":context-api"))
// litert-kmp: LiteTool интерфейс (sync describe/invoke)
+2 -1
View File
@@ -25,7 +25,8 @@ kotlin {
implementation(libs.kotlinx.serialization.json)
api(project(":context-api"))
api(project(":message-store-api"))
api(project(":journal-api"))
api(project(":reflection-api"))
// :context-api ссылается на Content / MessageContext из
// :message-log-api (старый canonical). Транзитивно через api,
// но фиксируем явно чтобы тестовый код видел Content без
@@ -4,7 +4,7 @@ import kotlinx.serialization.json.Json
import pw.binom.agentik.context.ContextStore
import pw.binom.agentik.context.WorkingMemoryEntry
import pw.binom.agentik.context.WorkingMemoryRow
import pw.binom.agentik.messageStore.Ids
import pw.binom.agentik.journal.Ids
import pw.binom.db.ksqlite.SQLiteConnection
import pw.binom.db.ksqlite.SQLitePreparedStatement
import kotlin.time.Clock
+2 -1
View File
@@ -19,7 +19,8 @@ kotlin {
sourceSets {
commonMain.dependencies {
api(project(":memory-api"))
api(project(":message-store-api"))
api(project(":journal-api"))
api(project(":reflection-api"))
api(project(":context-api"))
api(project(":skills"))
api(libs.litert.api)
@@ -10,7 +10,7 @@ import pw.binom.agentik.memory.MemoryStore
import pw.binom.agentik.memory.MemoryStoreEvent
import pw.binom.agentik.memory.NewMemoryNote
import pw.binom.agentik.memory.ReviewedTurn
import pw.binom.agentik.messageStore.Ids
import pw.binom.agentik.journal.Ids
import pw.binom.litert.LiteLlm
import kotlin.time.Clock
import kotlin.time.Instant
@@ -5,8 +5,8 @@ import kotlinx.coroutines.withContext
import pw.binom.agentik.memory.ConversationTurn
import pw.binom.litert.LiteConversationConfig
import pw.binom.litert.LiteLlm
import pw.binom.agentik.messageStore.Ids
import pw.binom.agentik.messageStore.Reflection
import pw.binom.agentik.journal.Ids
import pw.binom.agentik.reflection.Reflection
import kotlin.time.Clock
/**
@@ -57,7 +57,7 @@ class LlmReflector(
val parsed = ReflectionParser.parse(raw)
?: return@withContext null
Reflection(
id = Ids.reflection(),
id = pw.binom.agentik.reflection.Ids.new(),
conversationId = null, // будет проставлен caller'ом ChatConversation
createdAt = clock.now(),
turnsAnalyzed = turns.size,
-39
View File
@@ -1,39 +0,0 @@
plugins {
alias(libs.plugins.kotlin.multiplatform)
alias(libs.plugins.kotlin.serialization)
}
// Public API для append-only storage агента (audit log, replay-after-disconnect,
// conversation metadata). Этот модуль НЕ содержит working memory — он для
// тонких клиентов, которые хотят только события/сообщения без тяжёлой
// runtime-логики.
//
// Зависимости:
// - kotlinx.coroutines / serialization (KMP).
// - БЕЗ :storage-core — мы его полностью выпиливаем, интерфейсы переезжают сюда.
kotlin {
jvmToolchain(21)
jvm()
macosX64()
macosArm64()
iosX64()
iosArm64()
iosSimulatorArm64()
linuxX64()
linuxArm64()
mingwX64()
sourceSets {
commonMain.dependencies {
api(libs.kotlinx.coroutines.core)
api(libs.kotlinx.serialization.core)
api(libs.kotlinx.serialization.json)
}
commonTest.dependencies {
implementation(kotlin("test"))
implementation(libs.kotlinx.coroutines.test)
}
}
}
@@ -1,14 +0,0 @@
package pw.binom.agentik.messageStore
import kotlin.time.Instant
/**
* Snapshot диалога. В таблице `conversation` хранится как есть.
*/
data class ConversationRecord(
val id: String,
val title: String?,
val isTemporal: Boolean,
val createdAt: Instant,
val updatedAt: Instant,
)
@@ -1,27 +0,0 @@
package pw.binom.agentik.messageStore
import kotlin.time.Instant
/**
* CRUD по таблице `conversation`.
*/
interface ConversationStore : AutoCloseable {
/** Создать или обновить snapshot диалога. */
suspend fun upsert(record: ConversationRecord)
/** Диалог по id, или `null`. */
suspend fun get(id: String): ConversationRecord?
/** Удалить диалог (вместе с его сообщениями и working memory). */
suspend fun delete(id: String): Boolean
/** Список диалогов, отсортированный по `updatedAt` DESC. */
suspend fun list(offset: Int, limit: Int): List<ConversationRecord>
/** Переименовать диалог; `null` для сброса заголовка. Возвращает новый `updatedAt` или `null`, если не найден. */
suspend fun rename(id: String, title: String?): Instant?
/** Обновить `updatedAt` диалога (например, после отправки сообщения). */
suspend fun touch(id: String, now: Instant)
}
@@ -1,15 +0,0 @@
package pw.binom.agentik.messageStore
import kotlin.uuid.Uuid
/**
* Генератор id. Использует `kotlin.uuid.Uuid` из stdlib (KMP: jvm + native),
* чтобы не зависеть от `java.util.UUID` и подготовить код к linuxX64-сборке.
*
* Сохраняет формат `<prefix>-xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx` —
* `:server` его парсит как opaque string, без знания внутренней структуры.
*/
object Ids {
fun new(prefix: String): String = "$prefix-${Uuid.random()}"
fun reflection(): String = new("refl")
}
@@ -1,51 +0,0 @@
package pw.binom.agentik.messageStore
import kotlinx.coroutines.flow.Flow
import kotlin.time.Instant
/**
* Self-reflection запись — что агент "думает" о качестве своих последних ходов.
*
* - [score]: 1..5 (самооценка качества)
* - [weakSpots]: конкретные слабые места ("медленно ищу в Y", "путаю A и B")
* - [summary]: свободный комментарий в markdown (что заметил, что улучшить)
*
* Источник: one-shot LLM-размышление после каждых N ходов (см. AGENTIK_REFLECTION_INTERVAL).
* Используется в `ChatAgent.buildSystemPrompt` как "слабые места за последнее время".
*/
data class Reflection(
val id: String,
val conversationId: String?,
val createdAt: Instant,
val turnsAnalyzed: Int,
val score: Int,
val summary: String,
val weakSpots: List<String>,
)
/**
* Хранилище рефлексий. Backed by SQLDelight `reflection` таблицу (impl в
* `:storage-sqlite`) или in-memory (impl в `:storage-inmemory`).
*
* Рефлексии — append-only: старые записи удаляются [deleteOlderThan] (cleanup)
* или архивируются через [Curator]-подобный процесс, но не редактируются.
*/
interface ReflectionStore : AutoCloseable {
suspend fun insert(reflection: Reflection)
suspend fun get(id: String): Reflection?
/** Самые свежие рефлексии (по всему агенту). */
suspend fun listRecent(limit: Int = 10): List<Reflection>
/** Рефлексии для конкретного диалога. */
suspend fun listForConversation(conversationId: String, limit: Int = 10): List<Reflection>
suspend fun deleteOlderThan(cutoff: Instant)
suspend fun count(): Int
/** Стрим новых рефлексий для подписчиков (для UI в будущем). */
fun events(): Flow<ReflectionEvent> = kotlinx.coroutines.flow.emptyFlow()
override fun close()
}
sealed interface ReflectionEvent {
data class Created(val reflection: Reflection) : ReflectionEvent
}
+6 -3
View File
@@ -64,10 +64,13 @@ include(":memory-vector")
// Интерфейсы хранилища и разговорной истории: MessageStore / WorkingMemoryStore /
// ConversationStore / ReflectionStore + StorageBundle агрегатор. Реализации —
// в :storage-inmemory / :storage-sqlite / :storage-android (последний deferred).
// In-memory реализация store'ов из :message-store-api и :working-memory-api. KMP,
// In-memory реализация store'ов из :journal-api и :context-api. KMP,
// IO-зависимостей. Используется в тестах (быстрый setup, без JDBC) и будет
// использоваться в Android-сборке (JVector/SQLite не подходят для ART out-of-box).
include(":message-store-api")
//include(":message-store-api") — перенесено в :journal-api (ConversationRecord/ConversationStore/Ids)
// и :reflection-api (Reflection/ReflectionStore/ReflectionEvent/Ids)
// Модуль удалён с диска.
include(":reflection-api")
//include(":message-log-api")
//include(":message-log-ksqlite")
// Новые API-модули трёх сущностей (canonical имена):
@@ -90,7 +93,7 @@ include(":outbox-inmemory")
//include(":event-store-in-memory")
//include(":working-memory-api")
include(":storage-inmemory")
// SQLDelight-реализация store'ов из :message-store-api и :working-memory-api. JVM-only
// SQLDelight-реализация store'ов из :journal-api и :context-api. JVM-only
// KMP-реализация EventStore поверх ksqlite (https://github.com/caffeine-mgn/ksqlite).
// Цель: проверить что pure-Kotlin SQLite с sqlite-vec заменяет SQLDelight+JVector
// на KMP-таргетах (JVM + linuxX64 + mingwX64). Apple targets auto-disabled на
+2 -2
View File
@@ -41,10 +41,10 @@ kotlin {
commonMain.dependencies {
implementation(project(":proto"))
implementation(project(":server"))
implementation(project(":message-store-api"))
implementation(project(":journal-api"))
implementation(project(":outbox-api"))
implementation(project(":context-api"))
implementation(project(":reflection-api"))
// Commons
implementation(libs.kotlinx.coroutines.core)
@@ -67,10 +67,10 @@ kotlin {
if (!skipVectorMemory) {
implementation(project(":memory-vector"))
}
implementation(project(":message-store-api"))
implementation(project(":journal-api"))
implementation(project(":outbox-api"))
implementation(project(":context-api"))
implementation(project(":reflection-api"))
implementation(project(":storage-ksqlite"))
// Новый единый канал событий агента — заменил старые
@@ -9,7 +9,7 @@ import io.ktor.server.routing.post
import kotlinx.serialization.json.buildJsonObject
import kotlinx.serialization.json.put
import pw.binom.agentik.memory.ConversationTurn
import pw.binom.agentik.messageStore.ReflectionStore
import pw.binom.agentik.reflection.ReflectionStore
import pw.binom.agentik.proto.Agent
import pw.binom.agentik.standalone.agent.ChatConversation
import pw.binom.agentik.llm.tools.LlmReflector
@@ -317,7 +317,7 @@ private fun runServer() {
// и interval > 0. Загружаем top-K последних рефлексий из SQLite в system prompt.
val reflector: pw.binom.agentik.llm.tools.LlmReflector? =
if (config.reflection.interval > 0) LlmReflector(llm = llm) else null
val recentReflections: List<pw.binom.agentik.messageStore.Reflection> =
val recentReflections: List<pw.binom.agentik.reflection.Reflection> =
if (config.reflection.topK > 0) kotlinx.coroutines.runBlocking {
sqliteStores.reflections.listRecent(config.reflection.topK)
} else emptyList()
@@ -13,7 +13,7 @@ import pw.binom.agentik.memory.MemoryReviewer
import pw.binom.agentik.memory.MemoryStore
import pw.binom.agentik.skills.SkillStore
import pw.binom.agentik.journal.Content
import pw.binom.agentik.messageStore.ReflectionStore
import pw.binom.agentik.reflection.ReflectionStore
import pw.binom.agentik.context.WorkingMemoryEntry
import pw.binom.agentik.context.ContextStore
import pw.binom.agentik.llm.tools.LlmReflector
@@ -27,11 +27,11 @@ import pw.binom.agentik.skills.SkillCatalog
import pw.binom.agentik.skills.renderSystemPromptSection
import pw.binom.agentik.standalone.agent.memory.MemoryToolsFactory
import pw.binom.agentik.standalone.llm.LlmConfig
import pw.binom.agentik.messageStore.ConversationRecord
import pw.binom.agentik.messageStore.ConversationStore
import pw.binom.agentik.messageStore.Ids
import pw.binom.agentik.messageStore.Reflection
import pw.binom.agentik.messageStore.ReflectionStore
import pw.binom.agentik.journal.ConversationRecord
import pw.binom.agentik.journal.ConversationStore
import pw.binom.agentik.journal.Ids
import pw.binom.agentik.reflection.Reflection
import pw.binom.agentik.reflection.ReflectionStore
import pw.binom.agentik.journal.MutableJournalStore
import pw.binom.agentik.context.ContextStore
import pw.binom.agentik.toolsets.DisableToolsetTool
@@ -250,7 +250,7 @@ class ChatAgent(
override fun createConversation(temp: Boolean): ProtoConversation {
val now = now()
val id = pw.binom.agentik.messageStore.Ids.new("conv")
val id = pw.binom.agentik.journal.Ids.new("conv")
val rec = ConversationRecord(
id = id,
title = null,
@@ -25,13 +25,13 @@ import pw.binom.agentik.memory.MemoryStore
import pw.binom.agentik.proto.Content as ProtoContent
import pw.binom.agentik.proto.Conversation as ProtoConversation
import pw.binom.agentik.outbox.Event as ProtoEvent
import pw.binom.agentik.messageStore.ReflectionStore
import pw.binom.agentik.reflection.ReflectionStore
import pw.binom.agentik.proto.Message as ProtoMessage
import pw.binom.agentik.proto.MessageContext as ProtoMessageContext
import pw.binom.agentik.skills.SkillStore
import pw.binom.agentik.journal.Content
import pw.binom.agentik.messageStore.ConversationRecord
import pw.binom.agentik.messageStore.ConversationStore
import pw.binom.agentik.journal.ConversationRecord
import pw.binom.agentik.journal.ConversationStore
import pw.binom.agentik.journal.MessageContext
import pw.binom.agentik.journal.MessageOrigin
import pw.binom.agentik.journal.MessageRecord
@@ -482,7 +482,7 @@ class ConversationLoop(
private fun now(): Instant =
Instant.fromEpochMilliseconds(System.currentTimeMillis())
private fun newId(prefix: String): String = pw.binom.agentik.messageStore.Ids.new(prefix)
private fun newId(prefix: String): String = pw.binom.agentik.journal.Ids.new(prefix)
private fun encodeArgsJson(arguments: Map<String, Any?>): String {
val el = JsonElement.serializer()
@@ -1,7 +1,7 @@
package pw.binom.agentik.standalone.agent
import kotlinx.coroutines.CoroutineScope
import pw.binom.agentik.messageStore.ConversationRecord
import pw.binom.agentik.journal.ConversationRecord
import pw.binom.litert.LiteConversation
import java.util.concurrent.atomic.AtomicReference
import pw.binom.agentik.toolsets.NamedTool
@@ -5,7 +5,7 @@ import kotlin.test.assertFalse
import kotlin.test.assertTrue
import kotlin.time.Instant
import pw.binom.agentik.skills.SkillCatalog
import pw.binom.agentik.messageStore.Reflection
import pw.binom.agentik.reflection.Reflection
class ChatAgentReflectionTest {
@@ -1,7 +1,7 @@
package pw.binom.agentik.standalone.persistence
import pw.binom.agentik.journal.MessageContext
import pw.binom.agentik.journal.MessageOrigin
import pw.binom.agentik.messageStore.ConversationRecord
import pw.binom.agentik.journal.ConversationRecord
import pw.binom.agentik.journal.MessageRecord
import pw.binom.agentik.journal.Content
import pw.binom.agentik.context.WorkingMemoryEntry
+1 -1
View File
@@ -20,8 +20,8 @@ kotlin {
sourceSets {
commonMain.dependencies {
api(project(":message-store-api"))
api(project(":journal-api"))
api(project(":reflection-api"))
api(project(":context-api"))
}
commonTest.dependencies {
@@ -3,8 +3,8 @@ package pw.binom.agentik.storage.inmemory
import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock
import kotlin.time.Clock
import pw.binom.agentik.messageStore.ConversationRecord
import pw.binom.agentik.messageStore.ConversationStore
import pw.binom.agentik.journal.ConversationRecord
import pw.binom.agentik.journal.ConversationStore
import kotlin.time.Instant
/**
@@ -5,9 +5,9 @@ import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.asSharedFlow
import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock
import pw.binom.agentik.messageStore.Reflection
import pw.binom.agentik.messageStore.ReflectionEvent
import pw.binom.agentik.messageStore.ReflectionStore
import pw.binom.agentik.reflection.Reflection
import pw.binom.agentik.reflection.ReflectionEvent
import pw.binom.agentik.reflection.ReflectionStore
import kotlin.time.Instant
/**
@@ -2,8 +2,8 @@ package pw.binom.agentik.storage.inmemory
import pw.binom.agentik.context.ContextStore
import pw.binom.agentik.journal.MutableJournalStore
import pw.binom.agentik.messageStore.ConversationStore
import pw.binom.agentik.messageStore.ReflectionStore
import pw.binom.agentik.journal.ConversationStore
import pw.binom.agentik.reflection.ReflectionStore
import kotlin.time.Clock
/**
@@ -2,7 +2,7 @@ package pw.binom.agentik.storage.inmemory
import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock
import pw.binom.agentik.messageStore.Ids
import pw.binom.agentik.journal.Ids
import pw.binom.agentik.context.WorkingMemoryEntry
import pw.binom.agentik.context.WorkingMemoryRow
import pw.binom.agentik.context.ContextStore
@@ -1,6 +1,6 @@
package pw.binom.agentik.storage.inmemory
import pw.binom.agentik.messageStore.ConversationRecord
import pw.binom.agentik.journal.ConversationRecord
import kotlin.test.Test
import kotlin.test.assertEquals
import kotlin.test.assertNotNull
@@ -1,7 +1,7 @@
package pw.binom.agentik.storage.inmemory
import pw.binom.agentik.messageStore.Reflection
import pw.binom.agentik.messageStore.ReflectionEvent
import pw.binom.agentik.reflection.Reflection
import pw.binom.agentik.reflection.ReflectionEvent
import kotlin.test.Test
import kotlin.test.assertEquals
import kotlin.test.assertNotNull
+1 -1
View File
@@ -35,8 +35,8 @@ kotlin {
implementation("pw.binom.db:ksqlite:0.1.1-SNAPSHOT")
implementation(libs.kotlinx.serialization.json)
api(project(":message-store-api"))
api(project(":journal-api"))
api(project(":reflection-api"))
api(project(":context-api"))
}
commonTest.dependencies {
@@ -2,8 +2,8 @@ package pw.binom.agentik.storage.ksqlite
import kotlin.time.Clock
import kotlin.time.Instant
import pw.binom.agentik.messageStore.ConversationRecord
import pw.binom.agentik.messageStore.ConversationStore
import pw.binom.agentik.journal.ConversationRecord
import pw.binom.agentik.journal.ConversationStore
import pw.binom.db.ksqlite.SQLiteConnection
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.sync.Mutex
@@ -1,8 +1,8 @@
package pw.binom.agentik.storage.ksqlite
import pw.binom.agentik.messageStore.Reflection
import pw.binom.agentik.messageStore.ReflectionEvent
import pw.binom.agentik.messageStore.ReflectionStore
import pw.binom.agentik.reflection.Reflection
import pw.binom.agentik.reflection.ReflectionEvent
import pw.binom.agentik.reflection.ReflectionStore
import pw.binom.db.ksqlite.SQLiteConnection
import pw.binom.db.ksqlite.SQLitePreparedStatement
import pw.binom.db.ksqlite.SQLiteResultSet
@@ -2,8 +2,8 @@ package pw.binom.agentik.storage.ksqlite
import pw.binom.agentik.context.ContextStore
import pw.binom.agentik.journal.MutableJournalStore
import pw.binom.agentik.messageStore.ConversationStore
import pw.binom.agentik.messageStore.ReflectionStore
import pw.binom.agentik.journal.ConversationStore
import pw.binom.agentik.reflection.ReflectionStore
import pw.binom.db.ksqlite.SQLiteConnection
/**
@@ -4,7 +4,7 @@ import kotlinx.serialization.json.Json
import pw.binom.agentik.context.WorkingMemoryEntry
import pw.binom.agentik.context.WorkingMemoryRow
import pw.binom.agentik.context.ContextStore
import pw.binom.agentik.messageStore.Ids
import pw.binom.agentik.journal.Ids
import pw.binom.db.ksqlite.SQLiteConnection
import pw.binom.db.ksqlite.SQLitePreparedStatement
import kotlin.time.Clock
@@ -1,7 +1,7 @@
package pw.binom.agentik.storage.ksqlite
import kotlinx.coroutines.test.runTest
import pw.binom.agentik.messageStore.ConversationRecord
import pw.binom.agentik.journal.ConversationRecord
import kotlin.test.AfterTest
import kotlin.test.BeforeTest
import kotlin.test.Test
@@ -19,7 +19,7 @@ class KsqliteMessageStoreTest {
stores = KsqliteStores.inMemory("msg-${kotlin.random.Random.nextLong()}")
kotlinx.coroutines.runBlocking {
stores.conversations.upsert(
pw.binom.agentik.messageStore.ConversationRecord(
pw.binom.agentik.journal.ConversationRecord(
"conv1", null, false,
Instant.parse("2026-09-15T10:00:00Z"),
Instant.parse("2026-09-15T10:00:00Z"),
@@ -1,7 +1,7 @@
package pw.binom.agentik.storage.ksqlite
import kotlinx.coroutines.test.runTest
import pw.binom.agentik.messageStore.Reflection
import pw.binom.agentik.reflection.Reflection
import kotlin.test.AfterTest
import kotlin.test.BeforeTest
import kotlin.test.Test
@@ -88,7 +88,7 @@ class SchemaMigrationTest {
// bundle работает end-to-end — conversation upsert + message append +
// list. Никаких "no such table" или подобного.
stores.conversations.upsert(
pw.binom.agentik.messageStore.ConversationRecord(
pw.binom.agentik.journal.ConversationRecord(
id = "c1", title = "t", isTemporal = false,
createdAt = kotlin.time.Instant.parse("2026-09-15T10:00:00Z"),
updatedAt = kotlin.time.Instant.parse("2026-09-15T10:00:00Z"),