Введение монотонного offset'а как персистентного состояния агента:
offset'ы переживают рестарт standalone-агента, клиент продолжает
синхронизацию инкрементально, без полной re-sync с нуля.
outbox-api:
- Cursor (offset: Long) — курсор в журнале событий агента.
- OffsetSequencer — интерфейс резервирования уникального offset.
- CursorStore — персистентное хранилище текущего offset'а.
- PersistentOffsetSequencer — декоратор над любым OutboxStore,
обновляет CursorStore на каждом append (atomic transaction).
- OutboxGapException — клиент запросил after < earliestCursor() →
сервер не может удовлетворить, клиент обязан делать full resync.
- DurableEvent переименован из Event.kt → DurableEvent.kt (Event.kt
был общим sealed-типом, теперь это термин из спеки).
- MutableOutboxStore и OutboxStore теперь читают offset через
CursorStore вместо in-memory counter'а.
outbox-inmemory:
- InMemoryOffsetSequencer — для тестов и dev-режима.
- InMemoryOutboxStore теперь принимает OffsetSequencer в конструкторе.
outbox-ksqlite (новый модуль):
- KsqliteCursorStore — таблица outbox_cursor (agent_id TEXT PK,
offset INTEGER NOT NULL DEFAULT 0, updated_at INTEGER NOT NULL).
- KsqliteCursorStoreTest — 4 теста (set/get, monotonic, concurrent).
proto + server:
- Snapshot.proto — server-state snapshot endpoint для клиентов,
которым нужна полная материализация (использование TBD).
- Routes.kt + SnapshotRouteTest — endpoint /agentik/snapshot (GET).
journal-ksqlite:
- KsqliteJournalStore.listFlow/append — без изменений по API,
нотации минимальные (codecs).
standalone:
- DurableLog (бывший ChatAgent-orchestration) — атомарный commit
события в OutboxStore + PersistentOffsetSequencer + materialization
(через Reducer) одной транзакцией.
- SqliteStores — добавляет KsqliteCursorStore в bundle, единая
shared-connection для всех ksqlite-сторов standalone-агента.
- ChatAgent / ConversationLoop / ConversationEvents / ReflectionScheduler /
ToolDispatcher — переход на новые абстракции.
- standalone/build.gradle.kts — implementation(project(':outbox-ksqlite'))
включено (раньше было закомментировано — модуль только создавался).
client:
- AgentikAgent / AgentClient / HttpEventStore / HttpJournalStore /
ReconnectingOutbox — используют Cursor через transport API.
- client/README.md — синхронизирован с новым поведением (468 строк
diff — это в основном оформление и примеры).
kotlinx-io: 0.8.0 → 0.9.1 в libs.versions.toml (см. sync-core tests).
SYNC-SYSTEM.md (в корне) — спецификация, на которую ссылается и
:sync-core (эта сессия), и эта Cursor-абстракция в outbox-api.
Тесты: standalone 132, journal-ksqlite 25, outbox-inmemory 20,
outbox-ksqlite 4, client 10, sync-core 74 — все зелёные на jvm;
sync-core linuxX64 74 тоже зелёный.
sync2/ (заброшенный stub с одним build.gradle.kts) удалён.
:agentik-tui — Compose-for-Mosaic TUI-клиент к /agentik
Что это
Compose-style TUI-клиент в терминале на базе
Mosaic (Jetpack Compose
runtime, рендерится в ANSI-коды). Без :-команд (без vim-style
prompt): клавиатурная навигация Tab/Enter/Esc/Ctrl-D/F1/стрелки +
жирный focus indicator.
- Layout: header (id/conv/focus) + history + input + footer.
- Focus: Tab/Shift-Tab цикл по фокусам (input → history → sidebar).
- Input: стандартное текстовое поле с курсором
|посередине. - Stream: подписка на SSE в фон-корутинах,
StateFlow+collectAsState()для UI-реактивности (см. Snake sample).
Решает: полноценный TUI-клиент для тех, кто предпочитает мышкой
кликать в терминале больше, чем печатать. В отличие от :agentik-cli,
показывает историю диалога и текущий стрим в одном окне.
Как запустить
Требования
- JVM 21+.
- Запущенный
:standalone(по умолчаниюhttp://localhost:8080/agentik). - Реальный TTY (через
ssh -tt,tmux, либо нативный terminal).
Запуск из готового fatjar
java --enable-native-access=ALL-UNNAMED \
-jar agentik-tui-0.1.0-all.jar \
--server http://192.168.76.166:8080/agentik
--enable-native-access=ALL-UNNAMED обязателен — Mosaic использует
native syscalls для терминала.
Запуск через Gradle (dev)
./gradlew :agentik-tui:run --args="--server http://localhost:8080/agentik"
Параметры CLI
| Флаг | ENV | Что делает |
|---|---|---|
--server URL |
AGENTIK_SERVER |
URL /agentik (default http://localhost:8080/agentik) |
--id ID |
USER/USERNAME |
Имя агента (default — текущий пользователь) |
--no-history |
— | Не восстанавливать последнюю диалог после запуска |
--help |
— | Показывает help и выходит |
Keybindings
| Клавиша | Когда | Что делает |
|---|---|---|
Tab / Shift-Tab |
глобально | Цикл фокусов: input → history → sidebar → ... |
F1 |
глобально | Toggle help overlay |
Esc |
в input | Очистить input |
Enter |
в input | Submit message |
Backspace / Del |
в input | Удалить символ |
← → Home End |
в input | Курсор |
↑ ↓ |
в history | Scrollback |
Ctrl-D / Ctrl-C |
— | Exit (TODO — пока работает только вне стрима) |
Переменные среды (сервера)
См. ../standalone/README.md. TUI
получает URL сервера через --server, остальное настройка
агента, а не клиента.
Известное ограничение
- SSE в не-TTY ssh закрывается на default Ktor timeout — то
же, что для
:agentik-cli. - Mouse events не подключены в v2 (Mosaic 0.18 не имеет built-in mouse-runtime). Планируется в v3 через termios SGR-mouse.
- Нативные target'ы (macOS / Linux x64+ARM64 / Windows x64)
собраны, но без
:client(он JVM-only). Для нативной работы нужен альтернативный HTTP-клиент.
Тесты
./gradlew :agentik-tui:jvmTest
Тесты composable'ов и event-рендеринга. Включает smoke-test для key-event → AppState mutation → ре-рендер.
Версии
gradle/libs.versions.toml → [versions] agentik-agentik-tui.
Архитектурная заметка
UI-стейт держится в StateFlow, а не в Compose mutableStateOf.
Причина: Mosaic 0.18 не триггерит recomposition от mutableStateOf
-writes внутри onPreviewKeyEvent-handler'ов (см. Snake sample в
репо Mosaic — они тоже используют StateFlow + collectAsState()).