y83 code / журнал

Журнал релизов 0.8: часть 1 из 9

Полная ветка 0.8 из журнала проекта. Записи идут от свежих к старым, текст взят из CHANGELOG.md без правок — это тот же журнал, что читают разработчик и агент.

записей в ветке
251
приложение
185
бэкенд
59
архив сборок
7
период
19 августа 2026 — 24 октября 2026
страниц
1 из 9

журнал

Релизы ветки 0.8

Здесь записи 1–30 из 251: релизы приложения и бэкенда плюс записи архива сборок — каждая запись со своим набором правок.

app 0.8.185

24 октября 2026

Fixed

  • MCP-профиль в чужом проекте показывал инструменты y83-ws. Экран MCP брал mcpServerStatus у ещё не загруженного треда: app-server возвращал пустой список, а UI оставлял статусы предыдущего треда — подпись core, а tools те же 65+3. Теперь экран сначала поднимает тред (thread/resume) и только потом читает его инструменты. Хаб (без треда) показывает глобальный профиль, а не последний открытый тред.
  • Context7 снова подключается. Локальный прокси /mcp/context7 освобождён от локального токена (ключ Context7 подставляет бэкенд), иначе Codex получал 401 и терял три инструмента.

[Unreleased]

Fixed

  • После «Разрешить» бар больше не висит в «waiting for your answer». Ответ на карточку разрешения снимал её, но оставлял requires_action: статус ждал следующего запроса модели, то есть всю генерацию картинки. Теперь ожидание считается только по открытой карточке, ответ (и старт инструмента, который CLI разрешил сам) переводит ход в running, а имя MCP-инструмента в баре читается как y83 · image_generate.
  • Сгенерированная картинка видна сразу, без раскрытия «Работы». Вызовы image_generate/image_edit/image_status/image_cancel больше не входят в свёрнутую группу инструментов: у них отдельная карточка с галереей и просмотрщиком.
  • Тело скилла больше не выглядит сообщением человека. Claude пишет его в транскрипт как user-запись с turnCompanion/sourceToolUseID; история отбрасывает такие companion-записи (и не считает их ходом), при этом isMeta-записи с настоящим промптом рядом с <system-reminder> остаются.
  • У работающей карточки инструмента идут секунды. Время бралось только из tool_progress, а MCP-вызовы (в том числе image_generate) heartbeat не шлют — карточка молчала всю генерацию. Теперь незавершённый вызов тикает от метки старта, итог фиксируется по завершении, а heartbeat по-прежнему авторитетнее локального счётчика.

Changed

  • Смена режима, модели и уровня в Claude-чате читается плашкой. Раньше это была тонкая строка с Info и сырым значением CLI (Mode: acceptEdits). Теперь компактная плашка с иконкой настройки, переходом «было → стало» и временем; bypass подсвечен предупреждением. Чип режима в композере получил тот же вид: бордер, иконка, короткая подпись, у режима не по умолчанию — акцентный бордер.
  • Подтверждения remote-exec знают свой тред. Приложение берёт approvalId из результата remote_exec и привязывает заявку к конкретному Codex-треду: в уведомлении и карточке видно имя треда, тап ведёт ровно в него (включая фоновый тред через восстановление туннеля), а в диалоге сначала идут заявки открытого треда. Уведомления группируются по треду, два треда на одном сервере больше не сливаются в одно. Если привязки ещё нет (opencode-сессия, холодный старт) — «тред не определён» и тап в Approvals. Звук только на первое появление очереди, дальше тихие обновления и summary.
  • Меньше лишних подтверждений на серверах. Классификатор разбирает shell: безопасные ;, &&, пайпы, 2>/dev/null и read-only циклы выполняются сразу. Для изменяющих команд появился явный grant «разрешить сервер на 10 минут» с отзывом; секреты, SSH-ключи, firewall и критические операции всё равно требуют отдельного подтверждения.

Added

  • Режим native: Codex и Claude Code от собственной подписки. Экран «Аккаунты агентов» (хаб → AI & Agents) даёт по одному переключателю y83 | native на тип агента. В native CLI ходит напрямую по подписке (ChatGPT для Codex, Claude Pro/Max для Claude Code): роутер не подставляет ни адрес, ни токен, а из config.toml снимаются model_provider, model_catalog_json и секции y83_router. Политики, MCP y83 и context7 остаются. Вход выполняет сам CLI (codex login --device-auth, claude auth login): токены подписки наш код не читает и не проксирует. Пока режим native, а входа нет, ход не отправляется — чат показывает «вход не выполнен» вместо невнятного отказа, и тихого отката на роутер нет. Пикер моделей следует режиму: Codex берёт model/list у app-server, Claude — нативный список CLI. Стоимость хода в native не считается: подписка не даёт цены за токен.
  • Протокол на уровне модели: GPT Luna, Grok и Claude Haiku в одном провайдере. Шлюз opencode.ai смешивает пути: gpt-6-luna, gpt-5.6-luna, grok-4.6/4.7 и muse-spark-*contributor принимают только /responses, claude-haiku-5-5 и minimax-m2.7 — только Anthropic /messages. Раньше у провайдера был один protocol, и такие модели падали с ModelProtocolUnsupported; теперь у каждой модели есть своё поле protocol (пусто — наследовать провайдерский). Роутер пробует протоколы по очереди, но повторяет запрос только когда апстрим отверг именно формат (ModelProtocolUnsupported, 404/405) и пока клиенту не отдан ни байт: на 429/5xx и таймауты повтора нет, иначе квота и гейт жгли бы лишние запросы. 403 повторяется только с телом ModelProtocolUnsupported — шлюз помечает так неподдерживаемый формат; RegionError и FreeTierError с тем же кодом остаются окончательным отказом. Принятый протокол запоминается в модели, ручной выбор не перетирается. Для messages добавлен полный транспорт в обратную сторону (chat↔Messages API, включая SSE) и заголовки x-api-key + anthropic-version. Ответ конвертируется по фактически принятому протоколу, поэтому клиент всегда получает свой формат (choices или SSE Responses), даже когда автоподбор ушёл на другой путь.
  • Молчаливая потеря сообщений при конверсии. Внутри роутера один и тот же список живёт как []any (после JSON) и как []map[string]any (собранный responsesToChat); конвертер в Anthropic понимал только первый и отправлял в /messages запрос без сообщений («at least one message is required»).
  • Проверка доступности моделей провайдера. Кнопка «Проверить доступность» в шторке действий и листе моделей прогоняет каждую модель одним запросом на 1 токен (POST /api/v1/providers/{id}/models/probe, конкурентность 4, кэш час) и пишет в стор probe: работает ли, каким протоколом, а при отказе — причину (region, privacy, no upstream, protocol, auth). Бейдж виден в карточке модели и в диалоге discover; «all» больше не включает заведомо мёртвые модели. Так видно, что gemini-4-argon шлюз не проксирует ни одним протоколом, mimo-v2.5-pro отсутствует в апстриме, а iceman-contributor-free отвечает 503.
  • Claude Code: итог хода — «Изменено файлов». Под последним ответом появилась карточка со списком тронутых файлов и счётчиками +a −r; раскрывается в список, тап по файлу показывает его diff. Отдельного события «итоговый diff хода» CLI не присылает, поэтому карточка собирается из уже пришедших карточек Write/Edit/MultiEdit: файл считается один раз, повторы складываются по строкам, упавший инструмент не считается. Граница — последнее сообщение человека, а не вся история.
  • Claude Code: очередь сообщений и палитра /…. Набранное во время хода больше не уходит вторым ходом поверх первого: сообщение встаёт в локальную очередь над композером (правка тапом по тексту, «Отменить», «следующее») и отправляется само, когда CLI закрыл ход (turn_finished, а не status=idle — между ходами CLI ещё дописывает транскрипт). Предел — 20 сообщений, отказ виден в поле ошибки. «Отправить» появляется только когда ход уже не идёт: прерывать работающий ради очереди нельзя. В композере — палитра команд: список приходит из init.slash_commands самого CLI (встроенные команды, скиллы, плагины), раньше мост отдавал только счётчик. Команды, работающие лишь в терминале, помечены — в headless-режиме они отвечают «unavailable».
  • Claude Code: MCP-серверы и окно MCP. Агент получил настоящий MCP: ~/.claude.json → mcpServers.y83 с локальным токеном (профиль core, в этом репозитории — dev через --mcp-config), провижн идемпотентен и переживает ротацию токена. В меню чата — секция tools с пунктом «MCP servers» («подключено/всего»), окно claude@mcp показывает живые статусы из самого CLI (mcp_status): транспорт, версия, инструменты и текст ошибки, с Reconnect для упавших и тумблером вкл/выкл (действует до перезапуска процесса — CLI не пишет его в конфиг). init теперь не теряет mcp_servers: сводка видна ещё до открытия окна. Заголовки серверов (config с X-Y83-Token) мост вырезает и в приложение не отдаёт.
  • Claude Code: действия сессии в меню чата. Секция session окна «Chat settings» доделана до паритета с меню треда Codex: New session, Rename session, Session info, Keep screen on, Stop turn и Delete session. Переименование меняет имя в шапке сразу (POST …/title), Info показывает id, модель, режим и статистику транскрипта, удаление стирает сессию вместе с транскриптом и возвращает на список. Rename/Info/Delete гаснут, пока сессии нет в транскрипте, Delete — ещё и во время хода; мета секции показывает working/waiting.
  • Claude Code: меню шапки вместо иконок-действий. Справа в шапке чата теперь одна кнопка ⋮ — она открывает окно «Chat settings» в стиле меню треда Codex: модель, уровень рассуждений, режим разрешений, токены, затраты и список сессий. Уровни переехали в отдельное окно claude@reasoning (чип уровня в шапке ведёт туда же), режимы — в claude@mode, токены — в claude@tokens с разбивкой хода и сумм сессии. Stop и история ушли из шапки (Stop остался в композере), поэтому во время хода шапка не меняется, а длинное имя модели больше не ломает чип уровня по буквам.
  • Claude Code: токен-бар и история затрат. Над лентой — тот же бар, что в Codex: занятый контекст последнего запроса / окно модели, context %, цена хода по реестру тарифов и доля кэша; тап открывает окно затрат треда со ходами по дням. Ходы пишутся в turn_costs (scope claude) и видны в общем экране «Затраты»; старые сессии бэкфиллятся из транскрипта. Роутер теперь прокидывает cache_read_input_tokens и cache_creation_input_tokens в Messages API (без этого CLI видел только input/output и кэш терялся).
  • Claude Code: лента как в Codex — шаги, карточки инструментов, хвост. Работа агента (размышления + инструменты) собирается в сворачиваемые шаги «шаг N» / «шаги N–M» с временем и счётчиком; шаг живой — раскрыт, после завершения сворачивается и открывается тапом. Карточки инструментов теперь по виду инструмента: Bash (команда + stdout/stderr, длительность), Read (файл), Write/Edit/MultiEdit (путь, +N/−M, unified diff из structuredPatch), TodoWrite (чек-лист), Grep/Glob/WebFetch/WebSearch/Task и MCP — компактной строкой. Мост отдаёт компактную meta результата (stdout, stderr, structuredPatch, content, без originalFile), длительность инструмента по ts событий и thinkingDurationMs. Лента переведена на reverseLayout с общим авторитетом прокрутки Codex: автоследование за хвостом, пилюля «к последнему» со счётчиком новых ответов/ошибок, вставка в середине не рвёт позицию чтения.
  • Claude Code: разрешение над композером. Карточка Allow/Deny висит между статус-баром и полем ввода и видна при любой прокрутке; в ленте остаётся компактная строка решения. В снапшот добавлен seq, и SSE подписывается с Last-Event-ID, поэтому история не дублируется реплеем кольца.
  • Claude Code: выбор reasoning effort. CLI принимает --effort (low…max) и меняет уровень на ходу; мост передаёт его в POST …/effort, роутер — в reasoning_effort/reasoning.effort с нормализацией по модели. В шапке чата — чип auto/low…max с метром, в окне модели — секция // reasoning. output_config.effort из CLI раньше выбрасывался роутером, поэтому уровень не доезжал до провайдера.
  • Claude Code — третий агент, на наших провайдерах. Официальный CLI Anthropic ставится в Termux как glibc-бинарь (patchelf на $PREFIX/glibc/lib/ld-linux-aarch64.so.1, без Node и без bun-прослоек) и ходит в роутер через новый Anthropic-совместимый POST /codex-router/v1/messages. В приложении — свой чат: лента, размышления, карточки инструментов, разрешения Allow/Deny, выбор модели из каталога провайдеров, режим (manual/acceptEdits/plan/bypass), список сессий с продолжением и счётчики токенов. Мост — Go внутри ws-backend (backend/internal/claude), CLI запускается дочерним процессом.
  • Claude Code: экран проектов. Карточка агента теперь открывает проекты, как у Codex: папки из транскриптов CLI плюс метки (переименование, «убрать метку»), live-статус «идёт»/«ждёт разрешения», превью последнего вопроса, счётчик сессий и PullToRefresh. Сессии из терминала видны как живые (~/.claude/sessions), тестовые папки из $TMPDIR скрыты, сводки транскриптов кэшируются. Список сессий проекта получил поиск, группы по датам и удаление транскрипта (идущую сессию удалить нельзя — сначала Stop).
  • Claude Code: экран сессий как треды Codex. Карточки с плиткой-глифом, статус-строкой (таймер хода, «ждёт разрешения», «в терминале», «обновлено»), бегущей полосой во время хода, превью последнего вопроса и счётчиком токенов; меню Rename / Info / Stop / Delete. Список живёт: опрос раз в 3 с, пока экран открыт. Переименование пишется в транскрипт CLI (custom-title), поэтому имя видно и в claude --resume; окно Info показывает модель, папку, время, ходы и токены. Идущую сессию и сессию, занятую терминалом, удалить нельзя — бэкенд отвечает 409.
  • Claude Code: чат с шапкой и композером как в Codex. Шапка — та же градиентная вуаль: имя сессии, чип модели (окно выбора с поиском и группами по провайдеру; выбор запоминается для новых сессий), «Сессии» и Stop. Над полем — статус-бар (WorkingBar из Codex): этап (running: Bash · …, thinking…, waiting for your answer), таймер хода и полоса прогресса. Композер — карточка 20dp/тень 8dp с вложениями-чипами, счётчиком символов, expand/collapse, меню (Attach, Prompts…, Keep screen on, язык диктовки), меню режимов, голосом и Stop/Send по 40dp. Вложения идут путём в ~/y83-ws/uploads/ — агент читает файл сам, без правок моста.

Fixed

  • zen снова работает в Claude-чате. Free-tier гейт opencode.ai требует инструменты ровно bash и read в нижнем регистре, а Claude Code присылает Bash/Read: гейт находил их без учёта регистра, но не переименовывал (EqualFold считал имена совпавшими), и апстрим отвечал 429 FreeUsageLimitError. Теперь имя приводится к каноническому всегда, а вызовы возвращаются клиенту под его именами. Codex и opencode не страдали — у них имена уже нижним регистром.
  • Выбор модели в Claude-чате больше не подменяется. Окно брало каталог opencode (/api/v1/models), где встроенный zen называется opencode; роутер такого id не знал и молча подставлял первую модель списка — в шапке одно, исполнялось другое. Новый GET /api/v1/claude/models отдаёт каноничес- кие слаги роутера (zen/…, opencode-go/…, deepseek/…), а aliasModel подменяет только служебные claude-*; неизвестная модель — 404.
  • Отказ провайдера в Claude-чате виден и останавливает ход. 429 уходил в тихий ретрай CLI: полоса «работает» крутилась минутами, а причина не показывалась. Теперь system/api_error и синтетический assistant идут в ленту карточкой ошибки, ход завершается, а у CLI остаётся две попытки (CLAUDE_CODE_MAX_RETRIES). Messages-путь роутера лечит засвеченный лейн так же, как Codex: при FreeTierError/FreeUsageLimit лейн меняется и запрос повторяется.
  • Claude-чат не пишет «no connection» на новой сессии. Флаг связи поднимался только первым SSE-событием: у пустого чата и после обрыва потока шапка врала про обрыв, хотя REST работал. Теперь баннер только для живой сессии без потока, connected поднимается успешным ответом бэкенда, а потерянный поток переподключается на следующем ходе.
  • Отозванная minimax-m3.1-flash-free убрана из zen. Апстрим отвечает 410 ModelDeprecated; миграция zen.json v4 вычищает её из сохранённого каталога, сид и страховочная цепочка research её больше не берут.
  • Claude-чат падал при втором тапе по режиму. Служебные строки ленты («Модель: …», «Режим: …») строили id из счётчика, который не увеличивался: два переключения подряд давали один ключ, и LazyColumn падал с Key "mode-N" was already used. Теперь id уникальны, счётчик растёт.
  • Claude-режимы: весь набор CLI и честный bypass. В меню были четыре режима из шести — добавлены dontAsk и auto; bypassPermissions теперь работает: процесс стартует с --allow-dangerously-skip-permissions, а включение требует отдельного диалога подтверждения. Отказы CLI (control_response) больше не проглатываются: POST …/mode и /model отдают 502 с текстом, чип откатывается к прежнему значению.
  • Композер Claude: кнопки больше не выглядят одинаково. Меню композера перешло на иконку Widgets (как в Codex), а чип режима получил свою иконку для каждого значения и меню с заголовком // permission mode — его больше не путают с «выбором агента» (модель живёт в шапке).
  • Лента opencode-чата открывается с вопросами, а не стеной ответов. Страница в 50 сообщений у opencode — это 49 ответов агента и 0 запросов человека, поэтому чат открывался работой без контекста. Теперь стартовая загрузка идёт вглубь до двух запросов (не больше четырёх страниц), а курсор «показать более раннее» не откатывается при фоновом опросе.
  • Расход токенов больше не удваивается. session.tokens — уже накопленная сумма по сессии, но приложение прибавляло к ней сумму по сообщениям: окно токенов показывало двойной расход. Теперь источник истины — серверная сумма, а сумма по сообщениям осталась фолбэком для старого кэша.
  • Ошибка провайдера видна в ленте. Ответ с пустым content и полем error (лимит, отказ авторизации) выбрасывался как служебная запись: вопрос человека оставался без ответа и без причины. Теперь такой ответ рисуется блоком с текстом ошибки, а ход считается упавшим.
  • Кнопка diff под ответом агента работает. GET /diff?from= принимает только id запроса пользователя, а UI передавал id ответа — сервер отвечал «not a user message». Теперь id переводится на запрос, открывший ход.
  • Ретрай провайдера показывает попытку и причину. session.status.retry несёт attempt, message и next, но они терялись в парсере.
  • Аргументы инструмента видны ещё до запуска. Дельты session.tool.input.delta отбрасывались, и карточка до called оставалась с одним именем.
  • Шрифт opencode-чата следует настройкам чата, как в Codex. Блоки read, write, edit, диффы, вывод инструментов, окна терминалов, навыков, MCP, модели и меню сессии рисовались жёстко (bodySmall + Monospace + 11 sp), поэтому выбор шрифта и размера в настройках на них не действовал. Теперь все они идут через chatStyle(...): код — ChatTextType.Code, подписи — Meta, — а номера строк диффа масштабируются вместе с кодом.
  • Экран opencode-чата больше не смешивает языки. Английские хардкоды («How can I help?», «Changed files», «Session tokens», тосты об ошибках и вставке) переехали в res/values*.
  • Ответ в opencode-чате больше не задваивается после перезапуска приложения. Если приложение перезапускалось посреди хода, живая копия уже записанной в историю части оставалась в liveParts под другим messageID, и лента дорисовывала её вторым баблом (без подвала с временем и меню). Теперь live-сообщение не добавляется, если такая же часть уже есть в истории — сверка по ключу части, id вызова инструмента и полному тексту.
  • Карточки рассуждений в opencode-чате больше не «крутятся» все сразу. session.reasoning.ended приходил без статуса и с проигнорированным текстом, а живые части навсегда оставались стриминговыми: каждая мысль шага показывала спиннер и растущий таймер до конца хода. Теперь конец события фиксирует длительность и сворачивает карточку, а граница шага (step.ended/новый step.started) закрывает рассуждение и текст, если событие потерялось при реконнекте. Полоса хода перестала показывать «Thinking»/«Writing» после того, как этап закончился.
  • Роль сессии переключалась только в одну сторону (plan → build не срабатывал). Кэш экрана (session) не обновлялся после успешной смены роли, поэтому повторный выбор отсекался проверкой «сервер уже на этой роли»: чип показывал Build, а сессия оставалась в plan. Теперь после POST /agent серверная роль перечитывается и ложится в кэш, а события session.agent.selected синхронизируют его с другими клиентами.
  • Во всех проектах opencode-чата был один список сессий. Список запрашивался с ?location[directory]=…, но GET /api/session понимает только плоский ?directory=… и на deepObject фильтр молча игнорирует. Запрос исправлен, плюс клиент дополнительно отбирает сессии по каталогу (защита от старого кэша и версий сервера, которые фильтр не применяют); это же чинит «удалить сессии проекта», которое раньше могло унести чужие.
  • Каталог сессии снова читается (location.directory). v2 отдаёт его вложенным, а приложение смотрело плоское session.directory и получало null — из-за этого подписка SSE, каталоги навыков/MCP и счётчики сессий у проектов работали без каталога.
  • Архив сессий переехал на сервер (2.1.4). Отметка живёт в time.archived (PATCH /api/session/{id} {"archived":…}), а не в префах приложения: архив из TUI виден в приложении и наоборот. «Show archived» запрашивает ?archived=true, а старые локальные отметки один раз переносятся на сервер при первом запуске.

Changed

  • Страница проектов opencode стала живой, как список тредов Codex. Карточка показывает, сколько сессий проекта идёт и сколько ждёт ответа (GET /api/session/active + открытые permission/form; опрос раз в 3 с и только при видимом экране), бегущую полосу хода и превью — последнее сообщение пользователя самой свежей сессии. Живые проекты поднимаются наверх, дальше по свежести; в шапке — N sessions · M projects · K live. «~» теперь означает настоящую домашнюю папку Termux, а не workdir агента.

Added

  • Команды на удалённых серверах из Codex-чата. MCP-профиль /mcp/codex получил remote_servers, remote_exec, remote_exec_status и remote_job. Серверы берутся из реестра приложения (без паролей), read-only команды выполняются сразу, изменяющие ждут подтверждения в приложении (точная сохранённая команда, TTL 10 минут), длинные уходят в фоновые job с логами и pid на сервере. В /mcp/remote и opencode инструментов нет.

app 0.8.184

24 октября 2026

Added

  • Баланс DeepSeek в API providers. Карточка показывает доступность аккаунта и итог по каждой валюте без конвертации; шторка Balance — total, granted, topped up и время обновления. В меню добавлены «Balance…» и «Refresh balance». available=false показывается как insufficient, а не как ошибка запроса.

backend 0.8.341

24 октября 2026бэкенд

Added

  • Квота DeepSeek: баланс аккаунта. QuotaClient распознаёт DeepSeek по slug или хосту api.deepseek.com, читает официальный GET /user/balance ключом провайдера и отдаёт available + balances[] со строками currency, total, granted, toppedUp. Ошибки идут с префиксом balance:, кэш 45 с, singleflight и stale — общие с usage; история окон для баланса не пишется.

app 0.8.183

24 октября 2026

Changed

  • Полоска моделей в карточке провайдера стала навигацией. Тап по сводке со стрелкой открывает форму провайдера сразу на шаге моделей. Отдельная шторка моделей удалена — вместе с ней ушёл сценарий, где Save был за пределами экрана.
  • Форма провайдера перестроена: подключение → модели → тест и сохранение. API-ключ переехал на первый шаг, Save закреплён внизу и доступен с любого шага; тест больше не обязателен для сохранения.
  • Строки моделей компактные. Видны имя и окно контекста; редактор контекста и флаги tool calls/reasoning раскрываются по «настроить».

Added

  • Ручные vision-модели выбираются на экране Vision. Кнопка «+ модель» открывает поиск по моделям провайдеров; добавление ставит ручной флаг vision, а основной слот и страховщик назначаются отдельно. Ручную модель нельзя убрать, пока она стоит в цепочке; встроенный Zen в пикере скрыт.

backend 0.8.336

24 октября 2026бэкенд

Fixed

  • Смена ключа OpenCode Go доходит до opencode-чата. opencode v2 хранит API-ключ в собственной SQLite (таблица credential), а синхронизация писала только legacy auth.json — после ротации ключа в API providers чат продолжал ходить старым (исчерпанным) ключом и падал с «Go usage limit exceeded». Теперь SyncOpenCode применяет ключ через API v2 (connect/key + удаление устаревших credential), повторяет попытку после автостарта opencode и вычищает ключ при выключении провайдера.

backend 0.8.335

24 октября 2026бэкенд

Fixed

  • Окно выбора модели opencode показывает только выбранные провайдеры и модели. /api/v1/models отдавал весь каталог opencode (models.dev и встроенные провайдеры) — в окне были все 40 моделей, хотя в API providers включены два провайдера и три модели. Теперь каталог сверяется со стором провайдеров: выключенные провайдеры и неотмеченные модели не попадают в список; встроенный zen в каталоге называется opencode, поэтому для него проверяются оба имени. Пустой стор (первый запуск) по-прежнему отдаёт каталог целиком, чтобы окно не осталось пустым.

app 0.8.182

24 октября 2026

Changed

  • Окно затрат треда оформлено как дашборд. Терминальная шапка получила подзаголовок с названием треда и числом ходов, а плоский список заменён на сводку с итогом, средней ценой, долей кэша и числом ходов без тарифа. История сгруппирована по дням с подытогом; каждый ход — карточка с моделью, временем, ценой и разбивкой токенов. Общий экран затрат остаётся прежним.

Fixed

  • Отправка во время хода снова работает. Стрелка была активна, но sendInput в ChatScreen сам блокировал отправку при busy и не доходил до viewModel.send, где промпт ставится в очередь (delivery=queue) — текст молча терялся. Guard убран; команды /… тоже уходят в очередь (POST /command с delivery=queue), а не отбрасываются.
  • Compact context даёт видимый результат. После сжатия перечитывается история: в ленте появляется строка «Контекст сжат», приходит сообщение, а токен-бар считает занятый контекст заново. У compaction-сообщения токены — это его собственный вызов (контекст ДО сжатия), поэтому бар больше не показывает старые цифры: занятым контекстом считается размер сводки, а кэш-доля скрыта до следующего хода.
  • Окно модели opencode подписано как в Codex. Заголовок — Model, секции — // model и // reasoning: раньше окно выглядело «как было», хотя порядок и строки reasoning уже были перестроены. Префикс // рисует компонент заголовка, поэтому в подписи секции его больше нет (было // // model).
  • Вопрос-форма opencode больше не заперт в узком окне. Карточка переехала в ленту последним элементом (как вопрос Codex) и потеряла потолок высоты: длинная форма прокручивается вместе с чатом, а не внутри тесного блока на 280dp, где приходилось мотать и мотать. Композер остаётся на месте, ответы формы переживают прокрутку к старым сообщениям (состояние вынесено из элемента LazyColumn в MessageList).

app 0.8.181

24 октября 2026

Added

  • Выбор модели в чате opencode — как в окне «Model» Codex, и он запоминается. Окно перестроено по образцу Codex: сначала секция моделей (фильтр — только на длинном каталоге), затем уровни reasoning строками с пояснением и метром усилия («Авто» — провайдер решает сам). Выбор модели больше не закрывает окно: рядом сразу правится уровень. Совпадение выбранной строки считается по паре «id + провайдер», поэтому одноимённые модели разных провайдеров не подсвечиваются обе.
  • Новый чат стартует с последней выбранной модели и уровня. Выбор запоминается на агента (ChatModelMemory, ключ opencode_model_<agentId>): «+» в списке сессий создаёт чат сразу с этой моделью и variant (в v2 модель задаётся при создании), а сессия без модели при открытии получает выбранную — раньше шапка показывала big-pickle, а сервер отвечал своим дефолтом. Пропавшая из каталога модель/уровень сбрасываются на дефолт сервера, память при этом чистится.

Fixed

  • Вопрос-форма opencode оформлена как вопрос Codex — и на неё снова можно ответить. Прежняя карточка рисовала варианты чипами и не была ограничена по высоте: форма из 5-6 вопросов (плюс открытая клавиатура) вытесняла поле ввода и кнопку «Ответить» за край экрана — вопрос выглядел зависшим, ответить было физически нечем. Теперь карточка стоит над композером с потолком 45% оставшегося места (максимум 280dp), список полей прокручивается внутри, а заголовок и кнопки закреплены; варианты — строки с радио/чекбоксом и описанием, как в Codex, «свой ответ» — отдельным полем.
  • Вопрос больше не блокируется скрытым уточнением. Условные поля (when) не показывались, но считались обязательными — кнопка «Ответить» оставалась выключенной навсегда. Теперь невидимое поле не участвует в проверке, а в тело ответа уходят только видимые поля (скрытое сервер отверг бы FormInvalidAnswerError).
  • Вопрос можно пропустить. Кнопка «Пропустить» снимает форму через DELETE /api/session/:id/form/:id — раньше неудобный вопрос висел над композером до конца сессии, потому что ответить можно было только вариантом. Заодно карточка подставляет default из формы и показывает счётчик, если форм несколько.

app 0.8.180 / backend 0.8.334

24 октября 2026

Added

  • «Сжать контекст» теперь в меню композера opencode. Пункт рядом с Attach photo/file (иконка Compress): тот же сценарий, что в меню сессии — подтверждение и POST /api/session/:id/compact. Пока идёт ход или уже запущена компакция, пункт погашен, чтобы не запускать второй раз.

Fixed

  • «Думаю…» больше не дублируется в ленте opencode-чата. Ожидание рисовалось в трёх местах сразу: псевдо-сообщение в конце ленты (точки + Thinking…), пустая карточка рассуждения с подписью «Думает…» и полоса «работает» над полем ввода. Псевдо-сообщение удалено, а карточка рассуждения появляется только с первым куском текста — единственный индикатор ожидания теперь полоса хода с таймером.
  • Чат показывает реальное окно DeepSeek free — 300K, а не 200K. /api/v1/models отдавал limit.context из каталога opencode (models.dev), где free-моделям до сих пор записано 200k, хотя апстрим принимает ~310k (пробы 21.10.2026). Codex уже жил на 300k из zen-конфига, а чат рисовал «200K ctx» и завышал процент контекста. Теперь для opencode/zen фасад поднимает окно до проверенного значения (deepseek-v4.1-flash-free, mimo-v2.6-flash-free — 300k), чужих провайдеров с тем же id не трогает.
  • «Показать более раннее» больше не висит в новой сессии. opencode отдаёт cursor.next даже на последней странице (запрос по нему возвращает 0 сообщений), и чат слепо рисовал кнопку. Теперь страница запрашивается с явным limit=50, а «есть более раннее» — только у полной страницы: недобор значит, что история началась.
  • Таймер в карточках инструментов и рассуждений больше не стоит на 0.0s. opencode не кладёт time в живые tool/reasoning-события, а UI подставлял System.currentTimeMillis() прямо в композиции — на каждой рекомпозиции (10 раз в секунду) якорь уезжал вперёд. Старт теперь штампуется один раз при первом событии, длительность завершённой карточки считается от него же; подпись стала 12sp, выровнена вправо и не дёргает строку.
  • Роли в чате opencode снова работают (build/plan и субагенты). Композер отдавал в POST /api/session/:id/agent подпись роли (Build), а v2 ищет агента по id (build): сервер молча сохранял неизвестное значение, и первый же ход падал с idle / outcome: failed / Agent not found: "Build" — выглядело как «после перехода на v2 ни одна роль не работает». Теперь выбор, иконка, @упоминание и подпись в чипе разведены: в сессию и в agents[] уезжает id, на экране показывается name. Заодно чинятся сессии, испорченные прежним композером: при загрузке каталога подпись в сессии заменяется на id (repairAgentSelection), иначе старая сессия оставалась нерабочей. В списке ролей — только primary-агенты (build/plan/orchestrator): субагенты адресуются через @, а не сменой роли сессии.
  • Смена роли не рисует фальшивый «Thinking…» и карточку «service step». На plan сервер кладёт в inbox служебную реплику synthetic («You are in Plan mode…»); приложение считало её промптом — поднимало busy по session.inbox.enqueued и показывало карточку очереди с «Отменить»/«В поле», хотя отменять нечего. Теперь synthetic не поднимает ход и не попадает в очередь.
  • «Ошибка» в итоге хода — только когда сломался сам ход. Упавший вызов инструмента (grep без совпадений, проверка с ненулевым кодом) больше не красит весь ход в «Ошибка» — он виден на карточке инструмента.
  • Токен-бар не прыгает. session.usage.updated в v2 — накопленный расход сессии, а не промпт шага; приложение подставляло его в «занято контекста» (60k → 800k → 60k при окне 200k). Контекст теперь ведут токены сообщения.
  • Сообщение можно отправить во время хода. Вместо молчаливого return промпт уходит в очередь (delivery: "queue") и появляется карточкой очереди.

app 0.8.180 / backend 0.8.331

24 октября 2026

Fixed

  • Update all больше не виснет на шаге packages. Бэкенд запускал установщик как curl … | bash: stdin скрипта был пайпом, и apt/debconf вычитывали из него остаток setup.sh — процесс замирал без TTY (ручной запуск из Termux работал, потому что stdin — терминал). Теперь скрипт скачивается в ~/.y83/setup.sh и запускается с </dev/null, установщик сам перезапускает себя из файла, если пришёл через пайп, а apt идёт с DEBIAN_FRONTEND=noninteractive, --force-confdef/--force-confold и таймаутом; в лог пишется каждая группа пакетов.

app 0.8.180 / backend 0.8.327

24 октября 2026

Fixed

  • Pairing токена больше не «молчит» после отказа. Разрешение Termux RUN_COMMAND запрашивалось один раз и запоминалось флагом: если человек отказал (или система сбросила разрешение после обновления), pairing не запускался уже никогда, и local token required оставался навсегда. Теперь запрос повторяется при старте, а в манифест добавлен <queries><package com.termux/></queries> — без него на Android 11+ пакет Termux не виден и проверка разрешения/установки молча падала.
  • Экран Setup показывает реальный статус токена. «Backend online» ничего не говорил о pairing: health открыт без токена. Добавлена строка Local token (бэкенд принимает токен приложения или нет) и ручная команда записи ~/.y83-ws/local-token, если диалог разрешения не появился.
  • Opencode перезапускается после смены токена. Codex оживал через config/mcpServer/reload, а opencode читает opencode.jsonc только при старте: после pairing он продолжал ходить со старым X-Y83-Token и получал 401 на каждом MCP-вызове.

app 0.8.179 / backend 0.8.325

23 октября 2026

Fixed

  • Свежая установка больше не получает 401 «local token required». Pairing локального токена шёл через Termux RUN_COMMAND, но приложение никогда не запрашивало это runtime-разрешение, а Termux по умолчанию его не выдаёт (allow-external-apps=false). Теперь установщик включает allow-external-apps, приложение спрашивает разрешение при старте и перекладывает токен сразу после выдачи; если права нет — пишет причину в лог вместо молчаливого отказа.
  • Конфиги агентов догоняют токен без ротации. Сторож токена не делал первого прохода: если app-server/opencode стартовали раньше, чем появился ~/.y83-ws/local-token, X-Y83-Token в конфигах не было вовсе. Теперь бэкенд синхронизирует заголовки при старте, а opencode.jsonc создаётся с сервером y83-ws, даже если файла ещё нет.
  • Пустые баблы в ленте Codex. Модель шлёт agentMessage с одним zero-width space (\u200b), который String.isBlank() не ловит. Лента теперь считает такие сообщения пустыми и не рисует; ключи дедупликации нормализуют невидимые символы.

app 0.8.178

23 октября 2026

Fixed

  • VPN снова подключается. Процесс :vpn падал в onCreate до первой записи в лог: App.init() в этом процессе поднимал RunsWatchService (foreground-сервис), а с Android 12+ старт из фона бросает ForegroundServiceStartNotAllowedException. Теперь App.init() запускает сводку запусков только в главном процессе, а RunsWatchService.start() не роняет вызывающего при отказе.
  • Экран VPN больше не висит на «Подключается…». Сервис отвечает connecting сразу после принятия запроса; если :vpn не ответил за 12 с, VpnManager показывает ошибку с кнопкой «Повторить» вместо бесконечного спиннера. Ошибка старта startForegroundService тоже видна в UI.
  • Смерть :vpn больше не молчит. В процессе сервиса установлен crash-handler (пишет stack trace в log-vpn.txt), а шапка service created пишется до App.init().
  • TUN не остаётся после отключения. Пересборка TUN под per-app политику теперь перепроверяет desiredOn/isDisconnecting после establish(): гонка «Отключить во время применения политики» больше не оставляет полуживой интерфейс, который система затем отзывает.

Codex

  • Неудачная генерация картинки больше не выглядит успешной. В codex 0.164 imageGeneration при ошибке приходит со status:"failed" и failure:{type:"usageLimitExceeded", limitId, resetsAt}, без savedPath. Раньше такая карточка показывала «изображение сохранено в хранилище Codex». Теперь видна причина («Лимит генерации изображений исчерпан», время сброса) или общий текст ошибки, а пока картинка ещё генерируется — «Генерируется изображение…». Ошибка сохраняется в кэше вместе с карточкой.

backend 0.8.323 / app 0.8.176

23 октября 2026бэкенд

Fixed

  • История со скриншотами больше не упирается в 10 МиБ. Роутер вырезает input_image/image_url из message.content, function_call_output и custom_tool_call_output до отправки провайдеру и подставляет текст [изображение не передано; файл: …] с готовым вызовом mcp__y83__ai_vision. Путь берётся из парного view_image/local_image по call_id; если его нет, исходные байты один раз сохраняются в ~/.y83-ws/downloads/screenshots/router-<sha256>.<ext> (имя детерминированное, кэш не нужен). call_id, порядок и остальная история не меняются. Откат — файл ~/.y83-ws/router-native-images.on или Y83_ROUTER_NATIVE_IMAGES=1.
  • zen-прокси принимает 32 МиБ вместо 10. Тело сверх лимита теперь получает явный 413 request body exceeds 32 MiB, а не обрезанный JSON с unexpected end of JSON input.
  • Vision отправляет WebP q95 с правильным MIME. Раньше стоял жёсткий data:image/jpeg независимо от байтов. Теперь WebP в бюджете проходит байт-в-байт, остальные форматы один раз переводятся в WebP без ресайза; бюджет 3 МБ держится шкалой качества q95→q75, ресайз — только крайняя мера. Ограничение площади 3 МП снято (квадраты Krea 2048×2048 больше не уменьшаются), остался лимит стороны 3072 px.
  • Скриншоты браузера по умолчанию WebP q95. Непрозрачные кадры сжимаются в WebP (WEBP_LOSSY на API 30+, WEBP на 26–29), кадры с альфой остаются PNG. Признак прозрачности — реальная альфа пикселей, а не hasAlpha() у ARGB_8888: иначе все кадры WebView оставались бы PNG. Бюджеты maxPixels/maxSide/maxBytes не изменились.

backend 0.8.318 / app 0.8.175

22 октября 2026бэкенд

Fixed

  • Локальный HTML снова открывается в браузере. WebView работал с allowFileAccess = false, поэтому file:// из чата давал пустую страницу, а browser_open его вообще отклонял. Теперь страница и её относительные CSS/JS отдаются через WebViewAssetLoader с домена appassets.androidplatform.net: файлы читаются в процессе приложения, сеть и токен бэкенда не нужны, песочница WebView не расширяется. Путь, недоступный приложению (Termux), по-прежнему копируется через бэкенд — но уже в cacheDir/local-pages, а не как file://. В адресной строке file:///… и абсолютный путь открываются как локальная страница, в поле адреса виден обычный путь, а не asset-URL.

Security

  • Обновление снова ставится. manifest.json и APK выведены из-под локального токена: установщик и старая версия приложения качают их без X-Y83-Token (раньше — install failed: server rejected). Имя APK из manifest.json санитайзится: только basename с .apk и реальный путь внутри каталога обновлений — path traversal и symlink наружу закрыты.
  • SQLite не открывает ~/.y83-ws. Базы с токенами и конфигами доступны только экрану Databases (X-Y83-User-Action: sqlite), агент получает 400.
  • Git: закрыт shell через config. Запись в git config (включая --local: core.fsmonitor, diff.external, filter.*) запрещена, разрешено только чтение. Заблокированы submodule, bisect, filter-branch, send-email, difftool, mergetool, upload-pack, daemon. GITLAB_TOKEN подставляется только сетевым командам (push/fetch/pull/clone/ls-remote).
  • Файловый API не пишет в .git. file_write/mkdir в любой .git (hooks/config/objects) отклоняются, чтение ~/.y83 закрыто.
  • dev_exec: -f/--file у grep/rg и --files0-from у sort проверяются как пути, включая инлайновые формы.
  • env/update требует явного действия X-Y83-User-Action: env-update (приложение шлёт); решение по dev-маркеру кэшируется при первом обращении.
  • Удалён мёртвый control-стек (internal/control, control_proxy, MCP control_*, ControlBridgeService/Token/Screen/Dialog, манифест) и GET /api/v1/cookies — сырые cookies никем не использовались.

backend 0.8.315

22 октября 2026бэкенд

Fixed

  • Ротация локального токена больше не ломает MCP. Приложение при pairing перезаписывает ~/.y83-ws/local-token, а конфиги Codex/OpenCode оставались со старым X-Y83-Token — все mcp__y83__* отвечали 401 до ручной правки. Теперь watchLocalToken в бэкенде раз в 2 с сравнивает отпечаток файла и при смене переписывает оба конфига (providers.SyncLocalTokenHeaders) и зовёт config/mcpServer/reload у живого app-server. Заодно ensureY83MCPAuth перестал добавлять пустую строку при каждом провижне — конфиг больше не «меняется» на ровном месте.

Security

  • Локальный токен стал обязательным и на loopback. 127.0.0.1:4100 не аутентификация: туда может постучаться любое приложение с INTERNET. NewSecurityConfig всегда создаёт ~/.y83-ws/local-token (0600) и требует X-Y83-Token/Authorization: Bearer; исключения — только /health, /mcp/remote и /codex-router/* (у них своя bearer-аутентификация). Файл перечитывается с кэшем 1 секунда, так что pairing применяется без перезапуска бэкенда.
  • Чтение файлов ограничено. /api/v1/fs/* читает только home, /storage, /sdcard, Termux usr и tmp; ~/.ssh, ~/.gnupg, ~/.codex, ~/.config/opencode, ~/.git-credentials, ~/.netrc, ~/.pgpass и весь ~/.y83-ws (кроме downloads/) закрыты, symlink не обходит запрет.
  • SQLite открывает только home/storage/tmp. dbPath вне этих корней (включая symlink) отклоняется, WITH ... DELETE и PRAGMA writable_schema больше не проходят через /query.
  • dev_exec не читает файлы вне репозитория. Аргументы-пути cat/head/ grep/rg/find/ls/git проверяются safeRepoPath, ~ разворачивается.
  • Git-хуки недоступны агенту. commit, checkout, switch, merge, rebase, reset, revert, cherry-pick, am, apply, clean, restore и stash (кроме list) запрещены; экран Git шлёт X-Y83-User-Action: git и эти команды выполняет.
  • webai/export и /import требуют явного действия X-Y83-User-Action: webai-export: сырые токены и cookies больше не отдаются одним POST.
  • Очередь моста не теряет команды молча: RunCommand/Send при переполнении возвращают ошибку bridge queue is full вместо таймаута.

Fixed

  • Codex и OpenCode получают токен автоматически: SyncCodex пишет [mcp_servers.y83.http_headers] X-Y83-Token, SyncOpenCode — headers в opencode.jsonc (именно он задаёт mcp-секцию). Иначе mcp__y83__* переставали работать после включения токена.
  • Приложение ходит с токеном: OkHttp interceptor на общем клиенте подставляет X-Y83-Token для локального бэкенда (remote/сторонние API не затрагиваются); при 401 один раз делает pairing через Termux RUN_COMMAND. Git-экран и импорт сессии WebAI шлют заголовок явно.
  • Скрипты используют общий scripts/local-token.sh: setup.sh, bridge.sh, verify-webai.sh, versions.sh, codex-upgrade.sh, remote-e2e.sh больше не получают 401.

backend 0.8.312

22 октября 2026бэкенд

Security

  • Локальная граница доверия 4100 закрыта от браузера и DNS rebinding. Добавлен middleware: Host только loopback (или явный bind), Origin только loopback с тем же портом, Sec-Fetch-Site: cross-site и text/plain-мутации отсекаются до хендлеров. При не-loopback bind без Y83_ALLOW_NONLOOPBACK=true бэкенд не стартует; с флагом включается bearer из ~/.y83-ws/local-token.
  • Git больше не исполняет произвольные команды. REST принимает только POST и применяет ту же политику, что MCP; запрещены глобальные -c (кроме status.renames=false), -C, --git-dir, --exec-path, --upload-pack/--receive-pack, ext:: и --output. Удаление ветки разрешено только явному действию экрана Git (X-Y83-User-Action).
  • FS не пишет в секреты и по symlink. WriteFile/WriteFileBytes/ Mkdir проходят guardedPath; ~/.ssh, ~/.y83-ws, ~/.y83, ~/.codex и системные каталоги Termux защищены; новые файлы — 0600, существующие сохраняют права; запись в symlink запрещена.
  • /sqlite/query стал строго read-only. DML, WITH ... DELETE, multi-statement, ATTACH/DETACH и PRAGMA writable_schema отклоняются; мутации остаются только в /sqlite/execute.
  • Bridge не отдаёт управление чужому Origin. WebSocket проверяет Origin, второй клиент получает 409 вместо вытеснения приложения, переполнение очереди — 429 вместо ложного queued: true; GET-шлюз /api/v1/commands/{name} удалён, app_prefs всегда маскирует секреты.
  • dev_exec больше не RCE. Убраны env/printenv, запрещены глобальные опции git, --output, --no-index, git branch -D, а safeRepoPath разыменовывает symlink.
  • Меньше каналов утечки: ai_export/ai_import убраны из профиля Codex, webai/vision читает только картинки внутри home, CORS-звёзды у git и session/import убраны; /processes/kill не убивает сам бэкенд.
  • CSRF без тела закрыт. Content-Type: application/json обязателен для POST/PUT/PATCH, включая пустое тело (POST /backend/restart больше не проходит без заголовка); пустой DELETE разрешён (браузер не может отправить его simple-запросом, а прокси opencode удаляет без тела); потоковые fs/upload и remote/fs/upload принимают только application/octet-stream/ multipart/form-data. Клиенты (приложение, setup.sh, скрипты) шлют {} с JSON-типом.

Fixed

  • PID-файл бэкенда пишется в ~/.y83-ws/backend/ws-backend.pid — туда, где его ищет backendctl, а не в репозиторий.

0.8.174

22 октября 2026

Removed

  • Qwen Code вырезан из продукта. Убраны экраны ui/qwen, транспорт data/qwen, тип агента qwen, сид агента, карточка в каталоге, уведомления, цели моста и управление сервисом. Бэкенд больше не поднимает qwen-супервизор и не отдаёт /api/v1/qwen/*; ?type=qwen в каталоге провайдеров отвечает 400. WebAI Qwen, vision-модели qwen3-vl и DashScope-пресеты сохранены.

Changed

  • Миграция Room 16 → 17 удаляет Qwen-агентов и связанные проекты, базы проектов и записи thread_index; остальные агенты не трогаются.
  • Старые qwen-провайдеры вычищаются из api-providers.json при старте бэкенда, оригинал один раз сохраняется в api-providers.json.bak-qwen.

Docs

  • Убраны упоминания Qwen Code из архитектуры, уведомлений и скилла бэкенда.

0.8.173

22 октября 2026

Added

  • «Пульт запусков» (хаб → AI & Agents → Запуски, тайл на главной, цель моста runs). Один экран отвечает, что идёт сейчас, что ждёт ответа и что упало: активные треды Codex (включая remote через туннель, а не только локальный app-server), сессии opencode с формами/разрешениями/очередью, запущенные задачи планировщика и автостарт-сервисы, которые не работают. Строка показывает источник, имя/проект, модель, состояние и таймер; тап ведёт в чат/тред/логи, «Стоп» прерывает ход и чистит очередь. Внизу — свёрнутое «Недавно» (50 завершённых запусков).
  • Сводное уведомление (Notifications → Runs, по умолчанию выключено): одна строка «N выполняется» с действиями Stop/Open по каждой строке.

Changed

  • Агрегат запусков собирает бэкенд (POST /api/v1/runs/query, POST /api/v1/runs/stop, GET /api/v1/runs), а не приложение: экран, уведомление и MCP-клиенты видят одинаковый снимок. Источник, который не удалось опросить, показывается строкой «Требует внимания», а не пропадает.

backend 0.8.306

22 октября 2026бэкенд

Fixed

  • Кэш /api/v1/runs больше не отдаёт null вместо пустых списков. Второй запрос в пределах TTL (3 с) возвращал "recent": null без includeRecent, а пустые items/degraded терялись при клонировании снимка. Теперь инвариант «пустой список — это []» держится и на кэшированном пути; добавлены проверки в TestSnapshotCachesWithinTTL.

backend 0.8.302

22 октября 2026бэкенд

Added

  • **/api/v1/runs/* и пакеты internal/codexrpc + internal/runs.** codexrpc — минимальный JSON-RPC-клиент app-server по WebSocket (thread/list, thread/turns/list, turn/interrupt, thread/queue/*), runs — агрегатор с TTL-кэшем 3 с и бюджетом сбора 2.5 с.
  • opencode.Client.DoJSON — запросы любого метода (interrupt, inbox, form/permission) с той же Basic-аутентификацией из svreg.

Changed

  • **RegisterRoutes принимает *scheduler.Scheduler.** Планировщик теперь создаётся до регистрации роутов: пульту нужен тот же экземпляр, что и /api/v1/scheduler/* (иначе отмена прогона шла бы в чужой Store).

0.8.172

22 октября 2026

Removed

  • y83runner удалён из репозитория. Отдельный CLI-раннер opencode-агентов (ws-backend runner) никто в продукте не вызывал, к remote agents он отношения не имел, а его тесты не собирались и не попадали в ворота. Снимок сохранён тегом archive/y83runner, описание — docs/tech/y83runner-archive.md.
  • Прямой SOCKS5-прокси 127.0.0.1:1085 удалён. Им пользовался только y83runner; VPN-туннель, терминал и приложение работают без него.

Changed

  • Неизвестные аргументы ws-backend больше не запускают второй сервер. Вместо молчаливого старта на :4100 команда печатает использование и выходит с кодом 2.
  • Ворота прогоняют все Go-тесты. Отдельная стадия go-test-all убрана: после удаления вложенного модуля go test ./... и ./internal/... — одно и то же.

backend 0.8.301

22 октября 2026бэкенд

Removed

  • y83runner и подкоманда ws-backend runner. Вложенный модуль, cmd/server/cli_runner.go, профиль xip и bridge-токен ушли из кода.
  • ws-backend --mode=remote. Remote-профиль не использовался: на Ubuntu ставится только codex app-server через internal/tunnel/setup.go.
  • Неиспользуемые зависимости. Вместе с runner из go.mod/go.sum удалены ACP/opencode SDK, MCP SDK, cobra, OTel и Prometheus.

Changed

  • Неизвестный аргумент — ошибка, а не запуск сервера. ws-backend с незнакомым аргументом печатает использование и выходит с кодом 2, поэтому устаревший скрипт ws-backend runner не поднимает второй экземпляр на :4100.

0.8.171

21 октября 2026

Fixed

  • Установка opencode больше не висит 120 секунд на /api/info 500. Готовность теперь проверяется и по живым /api/session и /api/model; после обновления бинаря сервис перезапускается даже если старый процесс ещё отвечает.
  • Ошибки установки не теряются. Неудачное скачивание codex/opencode увеличивает ST_FAILED, установка скиллов без sha256_skills больше не распаковывается, а setup.sh завершается ненулевым кодом и пишет в лог DONE:/FAIL: — UI видит завершение.
  • Миграция не убивает рабочий opencode без реестра. Если servers.json недоступен, opencode/codex не считаются легаси; restart_stale_services ловит и заменённый бинарь (/proc/<pid>/exe с (deleted)).
  • Деплой на agent.y83.ru стал атомарным для opencode/codex. Они заливаются как .new в общей транзакции, manifest.txt меняется последним; более новая версия на сервере сохраняется, а publish.sh синхронно обновляет apk и sha256_apk.
  • Откат деплоя восстанавливает версионный APK и не копит бэкапы. Rollback не прерывается на ошибке копирования, а старые .deploy-backup-* ротируются до трёх.
  • Alert-команды скрытой сессии снова работают. browser_confirm_alert отправлял пустое confirm, а browser_prompt_alert — текст в поле text, которое Kotlin не читал: любой accept превращался в «отклонить», а prompt всегда закрывался с пустым ответом. Go теперь шлёт confirm: "accept"|"dismiss" и prompt: "accept" + text.
  • JS-экранирование сведено к одной реализации. Локальные replace("'", "\\'").replace("\\", ...) в видимом браузере «съедали» апостроф после слэша и не экранировали \r и \u2028/\u2029; rule-key в pressKey подставлялся без кавычек и ронял ReferenceError. Всё идёт через toJsSafe/toJsString, keyCode — числом.
  • Навигация скрытой сессии не уходит в локальные адреса. shouldOverrideUrlLoading (main frame) и download listener проверяют адрес через BrowserUrlPolicy: редирект внешнего сайта на 127.0.0.1, приватную подсеть или file:// блокируется и пишется в navigationBlocked; загрузка файла из чужого места — в downloadBlocked.
  • Загрузка файлов в скрытой сессии ограничена allowlist. armFiles/attachFiles читали любой путь (/data/data/com.y83.ws/... включительно) — теперь разрешены только filesDir, cacheDir, внешний каталог приложения и Download.
  • browser_shot больше не висит до таймаута. MCP-инструмент отправлял в приложение команду browser_shot, а там такого имени нет — ветка handleBrowserCommand не имела else и молча не отвечала, поэтому мост ждал 60 секунд и падал с «app не на связи». Добавлен алиас browser_shot → browser_screenshot и явный ответ на неизвестную browser-команду.

Added

  • Текст ошибки копируется из плашки. Рядом с Dismiss появилась кнопка-иконка: она кладёт в буфер полный текст сообщения (включая строки, скрытые лимитом в 4 строки) и на полторы секунды показывает галочку.
  • cropSpace у browser_shot. crop по умолчанию остаётся в пикселях кадра; cropSpace="css" берёт координаты из marks/browser_find и пересчитывает их под профиль (на мобильном 390×844 кадр в density раз крупнее — раньше вырезался не тот угол).
  • format/quality доходят до видимой вкладки. browser_shot передавал их только в скрытую сессию, поэтому видимый браузер всегда отдавал JPEG.
  • Журналы видимой вкладки отдают entries — тот же ключ, что у скрытой сессии: читатели и browser_timeline больше не зависят от hidden.
  • Свой инстанс SearXNG настраивается в приложении (Инструменты → SearXNG). Движок searxng без адреса теперь честно возвращает «инстанс не задан», а не подменяет выдачу DuckDuckGo.
  • browser_upload и browser_downloads — инструменты для input[type=file] и журнала загрузок скрытой сессии.

Changed

  • Меню главной — только действия. Статусный блок agents / vpn / tunnels убран из выпадающего меню: остались Home settings, Updates и Setup.
  • Версия OpenCode видна в Updates. Блок versions показывает фактическую версию бинаря (через /api/v1/env/status), а не устаревшую запись versions.txt; при недоступном статусе остаётся прежний фолбэк.
  • Длинный снимок находит корень прокрутки. SPA с внутренним overflow-y: auto и body { overflow: hidden } больше не склеиваются из кадров первого экрана (window.scrollY там всегда 0).
  • Пустой кадр определяется точнее. isBlankFrame сканировал сетку с шагом 16 px и считал белый фон с мелким текстом «пустым», навсегда переводя сессию в аварийный режим; теперь построчный проход по пикселям, а окно после аварийного кадра возвращается за границу экрана.
  • Stealth-фингерпринт приведён к Windows-десктопу: WebGL-renderer через Direct3D11 вместо Linux-строки Mesa и orientation.angle = 0 вместо 90.
  • HTTP-UA без модели устройства. Android 13; POCO … Build/… — маркер бота после UA Reduction: модель уходит в sec-ch-ua-model, в UA остаётся Android 10; K.

0.8.170

21 октября 2026

Fixed

  • Кросс-доменный parent больше не ломает спуф во фрейме. Чтение parent.__y83langs в чужом origin бросало SecurityError, и desktop-часть обрывалась на середине (в консоли — ошибка Яндекса на match.html). Теперь чтение обёрнуто в try/catch, а флаг __y83desktopApplied ставится только после успешного прохода.
  • Мобильный профиль тоже патчит iframe. Раньше хук contentWindow ставился только в desktop-режиме, поэтому внутри about:blank/srcdoc оставались navigator.webdriver, пустые plugins и chrome: undefined. antibot() стал функцией и применяется к окну и к каждому фрейму.
  • Текст страницы больше не теряет обратные слэши. Результат evaluateJavascript разбирался ручным replace("\\\\", "\\"): \t в коде превращался в таб, \d — в d, а \\ схлопывался. Добавлен WebViewHelper.decodeJsResult (JSONTokener) и переведены скрытая сессия, видимый браузер, DOM/HTML, формы, localStorage и parseOverlayPage.
  • Сессия не закрывается посреди работы. Таймер простоя (5 минут) отсчитывался от последнего open/navigate, и активные state/execute_js/ screenshot его не продлевали. Теперь любая команда продлевает TTL.
  • isLoading не залипает. Если onPageFinished не приходил (отмена или таймаут загрузки), флаг оставался true навсегда и waitForIdle() ждал до таймаута. При ошибке загрузки состояние сбрасывается и пишется lastError.

0.8.169

21 октября 2026

Fixed

  • Desktop-спуф скрытой сессии доведён до согласованного набора. После первой правки антибот всё ещё видел телефон: navigator.platform оставался Linux aarch64 при Windows-UA, navigator.language был en при languages = ru-RU,…, а screen отдавал физический экран (1280×2772) при окне 1920×1080. Теперь screen в desktop-профиле — CSS-размер профиля, platform — Win32, language выводится из languages, добавлены landscape-ориентация и WebGL-рендерер.
  • navigator.webdriver больше не виден антиботу. В WebView это non-configurable собственное свойство: delete не работает, и _.has(navigator,'webdriver') давал present (failed). Спуф подменяет window.navigator прокси поверх копии прототипа — ключ исчезает из in и has, а navigator остаётся instanceof Navigator.
  • plugins/mimeTypes — настоящие PluginArray/MimeTypeArray. Прежний спуф подставлял массив чисел ([object Array], plugins[0] === 1), и sannysoft показывал Plugins is of type PluginArray: failed. Теперь пять PDF-плагинов и два MIME-типа строятся от прототипов текущего window.
  • iframe получает тот же спуф. document-start не приходит в about:blank/srcdoc-фреймы, созданные страницей: внутри iframe оставались dpr=3.25, maxTouchPoints=5 и пустые plugins (HEADCHR_IFRAME: FAIL). Добавлен хук contentWindow/contentDocument, который применяет desktop-спуф к окну фрейма.

Changed

  • Старые версии секции 0.8.168 сохранены ниже — APK 0.8.168 уже установлен, его правки (окно за экраном, единый stealthScript, стелс в headless-путях) входят и в 0.8.169.
  • Видимый браузер больше не подменяет hardware при «как на компьютере»: desktop-режим — это только UA, как «Request desktop site» в Chrome на Android. Подмена dpr=1/screen=1920×1080 при мобильном вьюпорте давала больше противоречий, чем скрывала.

0.8.168

21 октября 2026

Fixed

  • Скрытый браузер больше не виден на экране. Окно TYPE_APPLICATION_OVERLAY теперь ставится за правой границей экрана (x = screenWidth), а не в левом верхнем углу: вертикальный вынос часть прошивок подрезала, и сверху оставалась полоса. Полупрозрачный резерв alpha=0.02 удалён — при пустой выдаче окно один раз возвращается на экран полностью прозрачным (alpha=0). browser_overlay_state показывает windowX/windowY/windowAlpha/windowOffScreen.
  • Desktop-спуф скрытой сессии действительно применяется. Два IIFE склеивались без ;, и второй блок падал с (intermediate value)(...) is not a function: devicePixelRatio оставался мобильным, maxTouchPoints и userAgentData.platform врали. Теперь части разделены stealthScript(), а состояние честно разделено на spoofInstalled (скрипт зарегистрирован), spoofVerified (страница отдаёт dpr=1 и maxTouchPoints=0) и spoofActual (сырые метрики). screen спуфа берётся из размера экрана, а не из зашитых 1920×1080.
  • Стелс стоит на всех путях чтения. HeadlessFetcher и FetchOverlayActivity раньше не ставили маскировку вовсе; теперь общий installStealthScripts с фолбэком в onPageStarted. Headless-путь получил программный слой, общий снимок с белым фоном (captureWebView) и общие константы вьюпорта/сэмплов (OverlayDefaults) — выдача снимается одинаково всеми путями.
  • Переключение «как на компьютере» в видимом браузере теперь переставляет и document-start спуф, а не только строку UA: devicePixelRatio, screen и maxTouchPoints меняются вместе с ней.

Changed

  • Куки — один профиль. Документация больше не обещает «два набора кук»: CookieManager и хранилище общие для видимого и скрытого браузера.

backend 0.8.295

21 октября 2026бэкенд

Fixed

  • Research больше не оставляет агента с одними ссылками. Живой синтез (deep, thinking) занимает 32–53 с, а резерв под него был 30 с: медленный ответ модели срезался по дедлайну, и агент шёл читать источники вручную. Теперь резерв — треть бюджета (60 с при 180 с, 90 с при 300 с, 40 с при минимальных 60 с), а на коротком deadline не съедает весь поиск.
  • При провале/таймауте синтеза возвращаются evidence. В ответе research идут выдержки до трёх уже прочитанных страниц (до 1200 символов, с нумерацией как в sources), status=timeout|error и пояснение в notes. То же для linear-режима: он теперь применяет deadline_s и проверяет остаток перед синтезом, а не упирается во внешний ctx MCP.

backend 0.8.292

21 октября 2026бэкенд

Fixed

  • Окно контекста zen-моделей приведено к реальному. В zen.json у free-моделей стояло 1 000 000, хотя апстрим столько не держит: пробы 21.10.2026 — deepseek-v4.1-flash-free принимает 310k токенов и отвечает 400 на 320k, mimo-v2.6-flash-free принимает 300k. Codex из-за завышенного окна не компактил контекст до ~900k и врезался в отказ апстрима context_length_exceeded; после неудачного хода usage не возвращается, счётчик Codex замирает — визуально это выглядело как «лимит упал до 200k». Теперь известные модели получают реальные окна (300k / 1M у minimax), миграция схемы v3 снимает фиктивные 1M, каталог Codex перечитывается сразу после применения zen.json (а не только при сохранении экрана «Zen»).

backend 0.8.291

21 октября 2026бэкенд

Fixed

  • response.completed.usage больше не обрывает ход Codex. Codex 0.162.1 (codex-api/src/sse/responses.rs) разбирает input_tokens, output_tokens, total_tokens, input_tokens_details.cached_tokens и output_tokens_details.reasoning_tokens как i64; null в любом из них давал failed to parse ResponseCompleted: invalid type: null, expected i64. convertUsage в zen-прокси теперь собирает канонический объект: все ключи всегда есть, все значения — числа (null/отсутствие → 0, total_tokens при нуле = input + output), добавлен input_tokens_details.cache_write_tokens. response.completed отдаёт валидный usage даже когда апстрим не прислал его вовсе. Такую же форму (с cache_write_tokens) собирает chatUsage роутера. Тесты: zen_usage_test.go, zen_stream_test.go (строгая Go-структура i64, повторяющая поля Codex), internal/providers/router_usage_test.go.
  • Ворота больше не пропускают провал i18n и py-selftest. st_i18n и st_py_selftest возвращали код только последней команды, поэтому падение tools/i18n.py check (или первых self-тестов) не валило стадию; теперь каждая проверка возвращает код через || return 1.

Changed

  • Русские слова-признаки рассуждения — в ресурсах. CodexReasoningLeak читает их из res/values/strings_codex.xml (codex_reasoning_cues_ru, translatable="false") через ResText.array в момент вызова, как handoff_service_words и control_ime_keys; в Kotlin остались только английские cue.

Повторные записи одной версии в журнале сведены в одну: 5 таких повторов на весь журнал.