docs: per-module READMEs (run vs library) + root navigation hub
ci / JVM build + tests (pull_request) Failing after 54s

Every subproject now has README.md:
- 3 runnable modules (:standalone, :agentik-cli, :agentik-tui):
  quickstart, env table, parameters, known limits
- 11 library modules: what it is, which problem solves, how to
  wire it in, where versions live

Root README.md is the navigation hub (Quickstart, Modules table,
publish + CI/CD notes).

Also: ci.yml prunes the :memory-vector -x excludes now that
text-embedding-kmp artifacts are published to caffeine.

518 tests green.

Verified publish pipeline: :proto:publish to caffeine produces
pom.module + per-target klibs + sources for all 9 KMP targets.

🤖 Generated with [opencode]
This commit is contained in:
SubochevAV
2026-09-16 20:44:16 +03:00
parent 5ad972767d
commit 8f616f359f
22 changed files with 1125 additions and 890 deletions
+57 -57
View File
@@ -1,77 +1,77 @@
# :storage-core — `pw.binom.agentik.storage`
# `:storage-core` — контракт хранилища (KMP, jvm + native)
**Интерфейсы хранилища разговорной истории агента: `ConversationStore`,
`MessageStore`, `WorkingMemoryStore`, `ReflectionStore`.**
Без зависимостей от конкретной БД.
## Что это
## Какую проблему решает
Интерфейсы persistence-уровня для `:standalone`:
`:standalone` нужно сохранять диалоги между перезапусками, держать working
memory (сжатую историю, которую видит LLM), reflection-записи. Но привязываться
к конкретной БД (SQLite) в контракте нельзя — для Android подходит другая
история, для тестов — in-memory, для экспериментов с Postgres — третья.
- `MessageStore` — append-only история сообщений по диалогу.
- `WorkingMemoryStore` — rolling buffer текущего хода (`AssistantMessage`,
`ToolExchange`, `UserMessage`, system-prompt) для fast-recovery
при reconnect/relance.
- `ConversationStore` — метаданные диалогов (id, title, model,
timestamps).
- `ReflectionStore` — LLM-reflections (свободная форма заметок
хранителя).
`:storage-core` отделяет **что хранить** (контракт) от **где хранить**
(бэкенды — `:storage-sqlite`, `:storage-inmemory`, и в будущем `:storage-android`).
Агрегатор `StorageBundle` собирает все 4 стора разом.
Решает: позволяет запустить агента на Android-in-memory, на
desktop-SQLite, или на production-SQLite, не переписывая логику.
Контракт минимален и async-friendly.
## Что в контракте
## Где используется
- `:storage-inmemory` — для тестов и Android.
- `:storage-sqlite` — прод (Desktop / server / однодесктопный
Android-development).
## Как подключить
```kotlin
interface ConversationStore : AutoCloseable {
suspend fun upsert(c: Conversation)
suspend fun get(id: String): Conversation?
suspend fun list(offset, limit, orderBy): List<Conversation>
suspend fun delete(id: String): Boolean
fun events(after: Instant): Flow<ConversationStoreEvent>
}
interface MessageStore : AutoCloseable {
suspend fun append(message: Message, conversationId: String, workingMemoryIndex: Int?)
suspend fun listByConversation(conversationId: String, after: Instant?, offset, limit): List<Message>
suspend fun update(message: Message, conversationId: String) // правка + бамп updatedAt
suspend fun deleteByConversation(conversationId: String)
}
interface WorkingMemoryStore : AutoCloseable {
suspend fun append(conversationId: String, entry: WorkingMemoryEntry, index: Int)
suspend fun listByConversation(conversationId: String, after: Instant?): List<WorkingMemoryEntry>
suspend fun compact(conversationId: String, fromIndex: Int, summary: SummaryEntry)
suspend fun reset(conversationId: String)
}
interface ReflectionStore : AutoCloseable {
suspend fun append(conversationId: String, reflection: ReflectionEntry)
suspend fun recent(conversationId: String?, topK: Int): List<ReflectionEntry>
kotlin {
sourceSets.commonMain.dependencies {
api("pw.binom.agentik:storage-core:0.1.0")
}
}
```
`WorkingMemoryEntry` — `sealed interface`: `User`, `Assistant`, `ToolExchange`,
`Summary`. Каждый entry имеет `instant` (когда попал в working memory) и
`index` (порядковый номер для восстановления при компакции).
## Версии
## Подключение
`gradle/libs.versions.toml` → `[versions] agentik-storage-core`.
## Что в API
```kotlin
commonMain {
implementation("pw.binom.agentik:storage-core:$version")
// + бэкенд:
implementation("pw.binom.agentik:storage-inmemory:$version") // тесты
// или
implementation("pw.binom.agentik:storage-sqlite:$version") // прод
interface MessageStore {
suspend fun append(conversationId: String, message: Message): Unit
suspend fun after(conversationId: String, instant: Instant, limit: Int = 100): List<Message>
}
interface WorkingMemoryStore {
suspend fun save(conv: String, entry: WorkingMemoryEntry): Unit
fun load(conv: String): Flow<WorkingMemoryEntry> // cold flow
suspend fun clear(conv: String): Unit
}
sealed interface WorkingMemoryEntry {
val id: String
val date: Instant
class UserMessage(...) : WorkingMemoryEntry
class AssistantMessage(...) : WorkingMemoryEntry
class ToolExchange(val toolName: String, val toolArgsJson: String, val resultText: String, val wasCancelled: Boolean) : WorkingMemoryEntry
class SystemPrompt(...) : WorkingMemoryEntry
}
```
## Где смотреть версии
## Тесты
- `version` из `gradle.properties` (`version=0.1.0`)
- релизы: `https://git.binom.pw/subochev/agentik/releases`
Контрактные тесты (общие для всех имплементаций) — в `:storage-sqlite`
и `:storage-inmemory`.
## Сборка
## Чего здесь НЕТ
```bash
./gradlew :storage-core:build
```
- Никакого HTTP / SSE.
- Никакой конкретной БД. Backend'ы в `:storage-*`.
KMP-таргеты — полный набор. Зависимости — только `kotlinx-coroutines` +
`kotlinx-serialization`.
## Текущий статус
Используется продакшеном. Контракт зафиксирован после
interrupt-имплементации (см. [INTERRUPT-DESIGN.md](../../docs/INTERRUPT-DESIGN.md)).
@@ -59,7 +59,7 @@ class PayloadTest {
}
@Test
fun `legacy plain-array payload still decodes (backward compat)`() {
fun `legacy plain-array payload still decodes backward compat`() {
val legacy = "[" +
"""{"type":"text","body":"old message"}""" +
"]"