2c71542b13
- settings/: settings.json (агенты, тема, clientId), атомарная запись - persistence/: ConversationMetaRepository — группы, lastSeen, превью, watermark синхронизации (synced_at через PRAGMA table_info) - session/: AgentRegistry (параллельное подключение, деградация в офлайн), AgentConnection (журнал + мета + живой список диалогов conversationsFlow, refresh по SSE-потоку), ChatSession (history + backfill + live + preview, дедуп по id, 1 мс шаг watermark, syncMutex), LiveTurn - UI: рейка агентов, список диалогов (живое обновление), чат со стримингом, композер (Enter отправляет, Shift+Enter переносит, Ctrl+M — голос), окна «Агенты»/«Группы»/«Новый диалог», тёмная и светлая темы - voice/: mic-api → Silero VAD → Vosk на одном выделенном потоке; VoiceNative.warmUp() до skia (иначе SIGSEGV), модель в артефакте - markdown/: вендоренный рендер из ai/assistent - ошибки в баннер через AppState.reportError (CancellationException игнорируется) - тесты 103 (реальная SQLite + фейковый транспорт); против живого агента — RealServerIT, RealAppStateIT, RealSpeechIT - LIVE-RUN-NOTES.md: живой прогон в GUI и найденные/починенные баги
95 lines
6.1 KiB
Markdown
95 lines
6.1 KiB
Markdown
# Живой прогон: что нашли и починили (28.09.2026)
|
||
|
||
Первый прогон десктопа не на фейках, а на живом `:standalone`-агенте
|
||
(Qwen через vLLM на 192.168.88.135) с настоящим GUI: окно Compose,
|
||
клики/печать через `ydotool`, проверка скриншотов.
|
||
|
||
## Что проверено вживую
|
||
|
||
| Шаг | Результат |
|
||
| --- | --- |
|
||
| Приложение подключается к агенту | шапка: «Локальный агент» + зелёное «на связи» |
|
||
| Список диалогов | подтягивается с сервера (33 диалога), поиск/время/аватары на месте |
|
||
| «+» (новый диалог) | сервер вернул новый диалог, счётчик на сервере 33 → 34 |
|
||
| Отправка сообщения | текст ушёл, ответ LLM пришёл в UI, в истории обе реплики |
|
||
| Голос (`Ctrl+M`) | «идёт запись» (микрофон красный) → повторное нажатие → «Речь не распознана», **без падения JVM** |
|
||
|
||
Ход агента при этом длинный: ответ занял 0.5 с, а один разбор с длинным
|
||
промптом — 144 с; это важно для бага №1 ниже.
|
||
|
||
## Починенные баги
|
||
|
||
### 1. `:client`: `POST /messages` умирал по таймауту 15 с (upstream)
|
||
|
||
Сервер отвечает на `POST /conversations/{id}/messages` только по завершении
|
||
всего хода агента (LLM + тулы), а это минуты. Клиент шёл с дефолтным
|
||
request-timeout Ktor → любая длинная реплика обрывалась на 15-й секунде
|
||
(`RealServerIT` показал реальные 144 с).
|
||
|
||
Фикс в `agentik/client/.../ConversationClient.kt`: у запроса отправки
|
||
снят таймаут чтения (`noReadTimeout()`, тот же приём, что для SSE).
|
||
Регрессионный тест — в `RealServerIT`.
|
||
|
||
### 2. Конфликт skiko и нативных ASR-библиотек → SIGSEGV
|
||
|
||
Найдено при добавлении тестов голоса: если onnxruntime/Vosk инициализируются
|
||
**после** skia, JVM падает в C++-логах. В проде это был бы краш при первом
|
||
нажатии микрофона (окно-то уже нарисовано). Подробности и таблица порядка
|
||
загрузки — в `MIC-ASR-SEARCH.md` §8. Фикс: `VoiceNative.warmUp()` в `main()`
|
||
до `application {}`, плюс `forkEvery = 1` в тестах.
|
||
|
||
### 3. Список диалогов не обновлялся никогда
|
||
|
||
`AgentConnection.listConversations()` читал `agent.conversationStore.list(...)`
|
||
один раз — а встроенный кэш либы наполняется асинхронно (`listFlow`), так что
|
||
даже «один раз» приходилось на пустой кэш. Итог: сервер отдаёт 33 диалога, а в
|
||
UI список пуст, и обновить его было нечем.
|
||
|
||
Фикс: `AgentConnection.conversationsFlow` — опрос встроенного кэша с
|
||
warmup-циклом на старте + подписка на SSE-поток (`outbox.agentEvents`) для
|
||
появления/удаления диалогов, созданных другими клиентами, + явный `refresh()`
|
||
после своих create/delete/rename.
|
||
|
||
### 4. Enter не отправлял сообщение, хотя подсказка это обещала
|
||
|
||
Поле многострочное, Enter вставлял перевод строки. Фикс: `onPreviewKeyEvent`
|
||
в композере — Enter отправляет, Shift+Enter переносит строку (тогда подсказка
|
||
«отправка Enter» становится правдой).
|
||
|
||
### 5. Клик по нижней/верхней части поля ввода не фокусировал его
|
||
|
||
`BasicTextField` высотой в строку внутри `Box` с паддингами: клик мимо самой
|
||
строки текста фокуса не давал. Фикс: клик по всему боксу запрашивает фокус
|
||
(`clickable` + `focusRequester`).
|
||
|
||
### 6. Ложный красный баннер «StandaloneCoroutine was cancelled»
|
||
|
||
`refresh()` отменяет предыдущий job, а `runCatching { ... }.onFailure { error = it.message }`
|
||
показывал отмену корутины как ошибку пользователя. Фикс: `AppState.reportError()`
|
||
игнорирует `CancellationException` (все 11 мест переведены на него).
|
||
|
||
### 7. Микрофон с клавиатуры
|
||
|
||
Мышиную кнопку микрофона удобно не всегда, а для проверки голоса без мыши —
|
||
критично: добавлен `Ctrl+M` из чата (и подсказка обновлена).
|
||
|
||
## Как повторить
|
||
|
||
```bash
|
||
# 1. живой агент (в фоне, из папки проекта)
|
||
cd /home/subochev/common-projects/WORK/ai/agentik/standalone
|
||
AGENTIK_SKILLS_DIR=./agentik/skills AGENTIK_SYSTEM_PROMPT='Ты полезный ассистент.' \
|
||
OPENAI_BASE_URL=http://192.168.88.135:8001/v1 AGENTIK_DB_PATH=./agentik/db.db \
|
||
OPENAI_CONTEXT_WINDOW=115000 AGENTIK_MEMORY_DIR=./agentik/memory \
|
||
OPENAI_LLM_BACKEND=openai AGENTIK_PORT=8080 OPENAI_API_KEY=... \
|
||
OPENAI_MODEL=... AGENTIK_SOUL=./agentik/SOUL.md ./gradlew run
|
||
|
||
# 2. десктоп (окно)
|
||
cd /home/subochev/common-projects/WORK/ai/agentik-desktop
|
||
AGENTIK_DESKTOP_HOME=/tmp/opencode/ak-real ./gradlew run
|
||
|
||
# 3. интеграционные тесты против живого агента
|
||
AGENTIK_E2E_URL=http://127.0.0.1:8080/agentik ./gradlew jvmTest \
|
||
--tests '*RealServerIT' --tests '*RealAppStateIT' --tests '*RealSpeechIT'
|
||
```
|