Commit Graph

69 Commits

Author SHA1 Message Date
subochev 1b817aa9a6 Сохраняем прогресс просмотра и возобновляем с позиции 2026-09-07 01:09:44 +03:00
subochev 79a500150d Пейринг RayNeo: кнопка ручного (без QR) ре-паринга + debug-роуты + keep-screen-on
- MercuryBridge.pairClassicAuto: классический discovery → найти RayNeo по
  имени → createBond (без QR и без камеры очков) для случая «бонд слетел,
  очки в first-pairing». discoverGlasses — список найденных.
- MercuryPanel: кнопка «🔗 паринг (вручную)» рядом с QR-парингом
- DebugHttpServer: /mercury/pair, /mercury/pair/stop, /mercury/pair/classic,
  /mercury/unpair, /mercury/bonded, /mercury/discover, поля pairing в /status
- keep-screen-on, чтобы QR/состояние реально были на экране телефона
2026-09-04 03:08:38 +03:00
subochev f68ea10342 Сборка release: подпись ключом Binom (v2+v3) + таск assembleSigned
signingConfigs в app-phone/app-glasses берут кестор и креды из скилла
android-sign (вне git); без кестора release собирается без подписи.
assembleSigned — собрать оба приложения одним шагом.
2026-09-04 03:08:28 +03:00
subochev 5c86ed00b4 STT: очки распознают речь локально и шлют готовый текст (SttText) в телефон
- glasses: локальный ASR через общий pw.binom.asr (GlassesAsr/GlassesSttSession/SttModelProvider)
- protocol: SttText — очки присылают распознанный текст в телефон; телефон кидает его в LLM
  (старый SttAudio/StopStt оставлен фолбэком для старых очей)
- phone: SttStreamer делегирует общему ASR; GlassesServer роутит SttText в LLM
- lib-core: AudioPrep общий
2026-09-04 03:08:26 +03:00
subochev 0b3f9ff975 Mirror: медленный list(500) не роняет каталог и не дёргается в цикле
- отдельный defaultMirrorHttpClient (connect 15с / request 90с) + 1 повтор на list():
  тяжёлый GET /api/mirror?limit=N на сервере дольше общего 30с-таймаута и подёргивается (503);
- каталог: зеркала грузятся в отдельном try — сбой/таймаут не чистит уже загруженный каталог,
  просто временно без бейджей;
- детали: убрали тяжёлый list(limit=500) каждые 3 с (progress зеркала на сервере не живой, 0
  до конца) — в цикле опрашивается только дешёвый mirrorByItem.
2026-09-02 23:02:56 +03:00
subochev 40723fa584 Каталог: массовые действия (конвертировать/скачать/удалить), фильтр «только скачанные» и BackHandler вложенных экранов 2026-09-02 22:38:54 +03:00
subochev 728116662e Добавляем WatchProgressStore (watchprogress.json) и его тесты 2026-09-02 22:38:30 +03:00
subochev 0cfd92a68f Добавляем экран «Активность»: что сейчас конвертируется и скачивается 2026-09-02 22:37:31 +03:00
subochev f656f43e10 Сохраняем прогресс просмотра и возобновляем с позиции 2026-09-02 20:55:00 +03:00
subochev 1b28760c6e Добавляем в бейдж каталога статусы загрузки и готовности видео на очки 2026-09-02 19:53:21 +03:00
subochev b2e2d2697d Добавляем индикатор рассинхронизации звука и видео в статус-панели очков 2026-09-02 19:27:11 +03:00
subochev 92b483a5e3 Добавляем свёрнутый блок результата тула в чат с раскрытием по тапу 2026-09-02 02:49:48 +03:00
subochev edf7cf63f6 Внедряем единый тул-канал для локального LiteRT и удалённого LLM-клиента 2026-09-02 02:42:00 +03:00
subochev 4587e22b50 Приложение добавляет OpenAI-совместимый HTTP-сервер и KMP-обёртку LiteRT 2026-09-01 18:45:25 +03:00
subochev e18b82642f audio-sync: учитывать латентность линка очки→телефон
Очки штампуют sentTs в PlaybackPosition; телефон мерит one-way
латентность (receivedTs - sentTs, EWMA) и добавляет её к мастер-позиции
в sync(), снимая системный перекос, из-за которого аудио недокручивало
на величину транспортной задержки. Плюс правки тестов под
HARD_SEEK_THRESHOLD_MS=5000 (остались незакомиченными с прошлого фикса).
2026-08-30 05:08:37 +03:00
subochev b11879520e fix(phone): поднимаем порог жёсткого seek аудио 2000→5000 мс
Жёсткий seekTo при |audio-video|>2000мс перебуферизировал ExoPlayer и давал
слышимую тишину (0.5-1с) + прыжок позиции. На локальном файле обычная буферная
пауза вывода пробивала порог и зацикливала seek→пауза. Порог 5000мс: умеренный
рассинхрон закрывается плавной коррекцией скоростью (бесшумно). Плюс диагностика:
лог типа источника (локальный файл/стриминг S3) и STATE_BUFFERING ExoPlayer,
и пик diff в логе seek.
2026-08-30 04:50:37 +03:00
subochev 3af74851db feat(mercury): синхронизировать часы очков с телефоном на подключении
RayNeoMercury.syncTime() шлёт текущее время телефона на очки. Вызываем
на событии Connected, чтобы очки получали корректную метку сразу после
подключения — это устраняет первопричину клина лог-спула (очки стартуют
с ~1970 до NTP, Loki отвергает batch с устаревшим таймстемпом, 400).
2026-08-30 03:42:47 +03:00
subochev 6cd4a8de43 feat: управление яркостью/WiFi очков через Mercury + накопленные доработки
- Mercury (app-phone): управление яркостью (с авто-выкл. перед
  применением) и WiFi (скан/подключение/список сетей с паролем);
  добавлено право BLUETOOTH_ADVERTISE в манифест и runtime-запрос
  (без него GATT-сервер не рекламируется → очки не подключаются)
- логирование (lib-core core.log): BatchingLogCollector + LogSink +
  LogSpool + LokiLogSink; цепочка очки→телефон→Loki; lib-core log()
  переадресован в коллектор
- STT: Qwen3-ASR через pw.binom.asr:asr-qwen3-android, убран in-house
  whisper-фоллбек
- audio-sync: P-регулятор с насыщением вместо фиксированной скорости
- media-split: очки только video, телефон только audio (UI-чипы)
- Components: форматирование размера через Locale.ROOT
2026-08-29 23:06:52 +03:00
subochev 62d2f06797 soft sync audio via PlaybackParameters speed (rate-based, no seekTo jump)
Master = glasses (5s PlaybackPosition), slave = phone audio.
Replace aggressive seekTo-drift-corrector with smooth tempo correction:
- |diff|<=150ms (TRIGGER_BAND) -> speed 1f (hysteresis, no flutter)
- audio lags -> speed 1.02 (catch up)
- audio ahead -> speed 0.98 (slow down)
- |diff|>2000ms -> rare hard seekTo + reset speed 1f
- removed catch-up seekTo after +1s (was adding ~2s error)
- log now prints speed
AudioSyncPlayerTest: 18 cases (boundaries 150/2000, speed up/down/1f, play/pause).
2026-08-27 23:23:50 +03:00
subochev cf8c5ef604 ui(phone): переключение сессий ступенчатым свайпом (порог 120dp, ±1 сессия за жест) + крупнее текст сообщений (14→20sp) 2026-08-27 18:46:31 +03:00
subochev da8182443c stt(phone): SttDebug переведён на Qwen3-ASR-0.6B (отладочный прогон wav) 2026-08-25 21:30:09 +03:00
subochev 17385d1784 stt(phone): Qwen3-ASR-0.6B int8 вместо whisper-small (константа STT_BACKEND, hotwords тулсет/тулы/умеешь) — откат в одну строку (opencode по ТЗ) 2026-08-25 19:57:33 +03:00
subochev 98ac84ffe8 stt(phone): сегментация буфера на фразы перед whisper (лимит ≤30с) — длинные сессии распознаются целиком, фразы склеиваются пробелом (opencode по ТЗ) 2026-08-25 18:09:56 +03:00
subochev ac56e70283 stt(phone): VAD удалён (резал рваный микрофон очков) — полный буфер + trimEdges + хвост 120с + авто-финиш по тишине 30с (opencode по ТЗ) 2026-08-25 03:45:42 +03:00
subochev 6d22f56bd8 Регулятор «Высота» экрана очков (0..1, с учётом масштаба): SetVertical по протоколу, offset Y в кино/коннект/оверлее, слайдер на пульте 2026-08-25 02:11:01 +03:00
subochev 032424f513 Mercury: enable heartbeat on connect (keeps link alive), autolock endpoint 2026-08-25 01:15:00 +03:00
subochev 4556d3b6e4 Mercury: verify handshake echo, autolock endpoint, ringInfo-safe status 2026-08-25 00:54:31 +03:00
subochev b40e44c653 DebugHttpServer: Mercury BLE-эндпоинты (/mercury/status|connect|disconnect|screen/off|screen/on) 2026-08-24 23:24:07 +03:00
subochev d674b31a81 Внедрение rayneo-mercury: зависимость + MercuryPanel (BLE-тест: погасить/включить экран) 2026-08-24 23:19:37 +03:00
subochev c222a4ac76 stt(phone): полный текст по клику — whisper на всём буфере, VAD только для промежуточных фраз 2026-08-24 22:11:08 +03:00
subochev 22eb83fd17 stt: debug-дампы аудио-пути (очки: сырой микрофон в WAV; телефон: принятые PCM) по старту от stt_dump.marker 2026-08-24 05:58:59 +03:00
subochev c1c169036a stt: VAD-интерфейс (Sherpa боевая / Silero тестовая), PhraseRecognizer (Whisper), чистый full-text-тест на реальном Silero VAD 2026-08-24 05:43:29 +03:00
subochev 7557d7229f Очки: отмена слушания двойным тапом — SttCancel reason=cancel 2026-08-24 03:01:55 +03:00
subochev e24caaa684 Очки: разграничение SttCancel (user/timeout) — выход из режима при тишине 30с, микрофон закрывается при потере фокуса 2026-08-23 21:17:43 +03:00
subochev 706cc10d1e Очки: история диалога в свайпе + распознавание в LLM только по клику (касяки 1-3) 2026-08-23 20:15:21 +03:00
subochev 51049d56a8 Чат: автоименование сессии из первого сообщения
- Assistant.processInSession: безымянная сессия получает имя из
  первого сообщения (обреза 30 симв.), «беседка» — пустое
- ChatScreen: периодическая перезагрузка подхватывает и список
  сессий — шапка обновляет название без ручного переключения
2026-08-23 17:40:12 +03:00
subochev ce2d9c5448 Чат: клавиатура не вырезает верх окна (adjustResize + imePadding)
- MainActivity: windowSoftInputMode=adjustResize — окно сжимается,
  а не панорамируется вверх при появлении IME
- ChatScreen: Modifier.imePadding() — контент честно ужимается
  под высоту клавиатуры
2026-08-23 17:30:40 +03:00
subochev d585ddf576 Локальная LLM: конфиг автора + uses-native-library в <application>
- AndroidManifest: три uses-native-library (libOpenCL/libvndksupport/libcdsprpc)
  ВНУТРИ <application> — aapt2 8.9.2 отбрасывает тег на уровне <manifest>
- LocalLlm: enableSpeculativeDecoding=true, GPU/GPU/CPU бэкенды (как у автора)
- DebugHttpServer: HTTP-отладка ассистента без UI
- Log/PhoneApp: мелкие правки

Подтверждено: движок поднимается, локальный чат отвечает на HONOR AXGL024B05001337
2026-08-23 17:25:55 +03:00
subochev 75496dd35d Фикс локальной LLM: таймаут 300с для LOCAL (инициализация движка ~17с + генерация), CPU-бэкенд без спекуляции (MTP виснет на CPU, GPU-делегат падает), диагностика chat()
Проверено на железе (HONOR FCP-N49): Gemma 4 E2B отвечает
«Здравствуйте! Чем я могу вам помочь с вашими очками Viewmate?»
(15 чанков, 61 символ, ~19с генерации).
2026-08-23 04:38:02 +03:00
subochev 7527cf8848 LLM: интерфейс + две реализации (Qwen удалённо / Gemma 4 E2B локально) + переключатель
- LlmClient интерфейс; RemoteLlmClient (OkHttp, llm.binom.pw) и LocalLlmClient (LiteRT-LM, Gemma 4 E2B)
- LocalLlm: Gemma4E2B (SHA-256, GPU/CPU-бэкенды), buildLocalLlmPrompt (SYSTEM→systemInstruction, TOOL→user), GemmaModelDownloader (стриминг, прогресс, фолбэк источников), спекулятивное декодирование
- LlmPrefs (персистентность выбора), PhoneApp: фабрика по llmChoice, пересоздание ассистента, скачивание/отмена
- UI: GlassesInfoScreen — выбор сервер/локальная + прогресс; ChatScreen — индикатор активной модели
- Тесты: +12 (LlmLocalTest) — phone 154 зелёных
2026-08-23 02:52:22 +03:00
subochev 28d0f9b68d Агент: медиа-тулсет (search/info/play) на Jellyfin + скилл media
- MEDIA_TOOLSET + MediaToolExecutor: search (поиск с лимитом), info (детали), play (проверки+запуск)
- JellyfinMediaSource: обёртка JellyfinClient (search/item) с защитой userId
- agentPlayMedia: проверки (очки на связи, видео скачано на очки, звук на телефоне) → watch с позицией
- PhoneActions.watch(startPositionMs), JellyfinItem.ProductionYear
- скилл app-phone/skills/media.md (frontmatter: name/description)
- MediaToolsTest: 22 кейса — всего 142 теста зелёные
2026-08-22 21:42:53 +03:00
subochev c5cbc893ac Агент: механика скиллов и тулсетов (ядро 5 тул, подмена, невидимые enable/disable)
По ТЗ TASK-agent-tools.md:
- Skills/SkillRepository/SkillRegistry: frontmatter-скиллы из files/skills, индекс для промпта, ленивое read_skill
- Toolsets/ToolsetRegistry: активность на сессию в хиппе, таймаут 10 мин (инжект часов), префиксная маршрутизация, идемпотентные enable/disable
- AgentToolkit: сборка промпта (3 состояния), диспач, бесшовная подмена вызова на активацию, валидация аргументов
- Assistant: цикл тулов с невидимыми вызовами (MAX_LLM_CALLS=8, MAX_TOOL_CALLS=3)
- Тесты: 18 новых (AgentToolkitTest 17 + SkillsTest 6 + ToolsetRegistryTest 9) — всего 120, зелёные
2026-08-22 21:13:29 +03:00
subochev bbf7a020d4 Ассистент Порфирий: тула времени, сессии, чат-UI, голосовой цикл; BT-транспорт очки↔телефон
- BT (RFCOMM SPP 00001105) вместо WiFi: BtGlassesTransport/BtServerTransport, реконнект 3с
- PlaybackService (foreground, WAKE_LOCK) против морозов HONOR
- Ассистент: AgentTools (TimeTool ISO 8601), TOOL_CALL-протокол, сессии SQLite (UUID)
- Чат-UI: создание/сброс/переключение сессий, автообновление ленты 2с
- LlmClient на прямом OkHttp (Koog давал пустой content на длинных историях)
- Голосовой цикл: тройной тап на очках → STT → активная сессия → ShowText
- Тесты: AgentToolsTest (3), GlassesServerTest переписан (6) — зелёные
2026-08-22 16:31:04 +03:00
subochev ee9700c558 CICD: сборка APK на release → F-Droid; versionCode из тега; фикс скролла каталога и масштаба
build / Android APK to F-Droid (release) Failing after 36s
2026-08-21 19:27:02 +03:00
subochev ee86b9dc50 Revert "feat: retry конвертации (2 попытки) при failed/error"
This reverts commit cbcd22f09c.
2026-08-21 12:08:57 +03:00
Hermes Agent cbcd22f09c feat: retry конвертации (2 попытки) при failed/error
- polling-цикл: при status failed/error — retry (до 2 раз)
- логирование каждого retry
- после исчерпания retry — текущее поведение (кнопка 'Перезаказать')
2026-08-21 11:54:48 +03:00
Hermes Agent a434d5bb12 feat: потоковый локальный ассистент — фраза → LLM → ответ на очки
- agent/Assistant.kt: оркестратор диалога (SQLite-история + Koog), таймаут 30с,
  сессия одна живая, история — последние 20 сообщений
- ChatDao.latestSessionId(); SYSTEM_PROMPT нейтральный (TODO: персона)
- PhoneApp: очередь фраз (Channel UNLIMITED), assistantLoop (THINKING/ShowText/IDLE),
  ensureAssistant ленивый; ключ/модель из BuildConfig (local.properties)
- Очки: assistantText StateFlow, ShowText → оверлей (белый текст речи + серый ответ),
  оверлей остаётся после Click до новой сессии
- AssistantTest (androidTest, 3 теста: сессия/история/таймаут) — OK (6 tests всего)
- README: строка про ассистента
2026-08-20 12:35:49 +03:00
Hermes Agent d9d34e9277 feat: история общения (SQLite) + Koog LLM-клиент в app-phone
SQLite (голый SQLiteOpenHelper, без ORM — как решили):
- sessions (id, title, created_at, updated_at) + messages (role, content,
  tool_call_id, tool_calls JSON, attachments JSON, created_at) по утверждённой
  схеме; FK-каскад; CHECK на роли.
- ChatDb + ChatDao (createSession/listSessions/rename/touch/delete, addMessage,
  messagesForSession), доступ через PhoneApp.chatDao.
- Unit: ModelsTest (роли, JSON tool_calls/attachments). androidTest:
  ChatDaoTest — CRUD-круг + каскад — OK (3 tests) на эмуляторе.

Koog (JetBrains) — базовый LLM-клиент, без агентного слоя:
- ai.koog:prompt-executor-openai-client:1.1.1 + http-client-okhttp
- LlmClient: OpenAILLMClient(apiKey, baseUrl=llm.binom.pw) → chat(история)
- BuildConfig LLM_API_KEY/LLM_MODEL из local.properties (вне git); llm_debug
  smoke (--ez llm_debug true) — проверен на эмуляторе: реальный ответ от
  llm.binom.pw (deepseek-v4-flash).
- ЗАГЛУШКА Coroutines_jvmKt: дефект публикации Koog 1.1.1 — utils-android не
  включает jvm-класс, который зовёт OkHttpKoogHttpClient (без неё
  NoClassDefFoundError на первом вызове). Сигнатура сверена с utils-jvm.
  Убрать после фикса JetBrains.
2026-08-20 11:52:50 +03:00
Hermes Agent 3ed63012f4 build: Kotlin 2.2.0 → 2.4.10 (AGP 8.9.2, Gradle 8.14 — в официальном диапазоне)
- libs.versions.toml: kotlin = 2.4.10
- jvmTarget '17' (kotlinOptions, String) — в 2.4.10 ошибка → compilerOptions DSL
  (JvmTarget.JVM_17) в app-phone и app-glasses
- Compose-плагин (org.jetbrains.kotlin.plugin.compose) версионируется с Kotlin — 2.4.10
- Проверено: lib-core allTests + phone/glasses тесты + оба APK — BUILD SUCCESSFUL
- Открывает дорогу Koog (требует Kotlin 2.3.10+)
2026-08-20 10:51:07 +03:00
Hermes Agent 1304c6d921 feat: голосовой ввод на очках — TripleClick + PCM → телефон → текст на очки
Протокол: SttAudio (PCM s16 16k mono кусками), SttPhrase/SttDone/SttCancel
(host→очки), StopStt(cancel) (очки→host).

Очки: TripleClick — безусловный вызов (кино: пауза + затемнение + слушаем);
во время распознавания Click — завершить (весь текст в консоль, затемнение
уходит, видео на паузе), DoubleClick — отмена; VAD-тишина 30с — автоотмена.
Микрофон (AudioRecord 16k) → SttAudio; на эмуляторе — PCM из filesDir/samples
(куски 3200 + пауза 200мс). Оверлей текста в Binocular (полупрозрачный фон,
текст по центру, в оба глаза). RECORD_AUDIO в манифесте. DEBUG-ресивер
(am broadcast DEBUG_GESTURE type=triple_click|click|double_click).

Телефон: SttStreamer — стриминговый VAD (Silero fp32, окна 512) → фразы →
Whisper-small int8 (executor, не блокирует приём) → SttPhrase на очки;
StopStt → SttDone (полный текст) / SttCancel; watchdog 30с тишины.

Фиксы (найдены на эмуляторе): IllegalThreadStateException — двойной старт
watchdog-потока (thread{}.apply{start()}); decode входящих — try/catch с
логом (не роняет сессию).

Проверено на эмуляторах (5554 очки + 5556 телефон, forward 8080):
triple_click → фразы «Раз, два» и «3 4 5» на очках (оверлей) → click →
«ВЕСЬ ТЕКСТ: Раз, два 3 4 5»; 30с-тишина → «отменено». Тесты: lib-core,
phone, glasses — зелёные.
2026-08-20 04:12:28 +03:00