Files
agentik/agentik-tui
subochev 9e888227a3 refactor: outbox становится live-only стримом, cursor вынесен в :cursor-api
Семантика outbox'а — теперь чистый live-канал:
  * OutboxStore.conversationEvents(id) — больше не принимает after-cursor.
    Catchup (replay) делает клиент: journal.listFlow(afterSeq) + подписка
    на live. Outbox ответственен только за уведомления «что-то произошло».
  * OutboxStore.oldestCursor()/currentCursor()/OutboxGapException — удалены.
    Эпоха и offset живут ТОЛЬКО в CursorHolder/CursorStore, переживают
    рестарт и инкремент для каждого commit.
  * DurableEvent: commit принимает блок { cursor -> MessageRecord }
    (cursor выдаёт CursorStore; клиент не вычисляет offset сам).
  * MessageRecord больше не несёт cursor — это не его ответственность.

Новые модули:
  * :cursor-api         — Cursor(epoch, offset) + CursorHolder / MutableCursorHolder
  * :cursor-ksqlite     — KsqliteCursorHolder (персистентный)
  * :cursor-inmemory    — для тестов
  * :client-sync        — LocalSyncAgent (мини-агент поверх :client для десктопа)

Удалены:
  * :sync-core          — старая референсная реализация, заменена
                           cursor-разделением и :client.
  * outbox-ksqlite      — CursorStore/Schema уехали в :cursor-ksqlite.
  * OffsetSequencer / PersistentOffsetSequencer / InMemoryOffsetSequencer.

standalone:
  * ChatAgent/ConversationLoop/DurableLog/ToolDispatcher/ReflectionScheduler/
    ConversationEvents — подписка через push-паттерн (collect событий).
  * A2aBridge — currentCursor() и conversationEvents(after=) убраны.
  * SqliteStores — cursor_offset удалён из schema v4; seedNextFromJournal
    читает MAX(created_at).
  * Main.kt — outboxSequencer → outboxCursorHolder; user→agent (:server)
    transport удалён; debug-routes удалены; A2A остался.
  * Тесты ChatAgentTest/PersistenceTest переписаны на push-паттерн
    (subscribe-before-act, snapshot∪live = итоговое состояние). 25/25 + 19/19 ✅

server / client:
  * Routes эпоху читают из CursorHolder; снимки несут Cursor? для catchup.
  * AgentikAgent и HttpEventStore — те же подписки, без after-параметра.
  * ReconnectingOutbox / ReconnectingOutboxTest — без изменений API.
  * JournalStore API расширен count(after=Instant?) для unread-badge.
2026-10-05 23:33:35 +03:00
..

: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, остальное настройка агента, а не клиента.

Известное ограничение

  1. SSE в не-TTY ssh закрывается на default Ktor timeout — то же, что для :agentik-cli.
  2. Mouse events не подключены в v2 (Mosaic 0.18 не имеет built-in mouse-runtime). Планируется в v3 через termios SGR-mouse.
  3. Нативные 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()).