Новое сообщение об отказе agy показало причину. У рабочего аккаунта
victor.trushenko@gmail.com каталог получался (11 моделей), а вызов падал:
invalid model selection (--model "gemini-3.7-flash" --effort ""):
gemini-3.7-flash requires --effort (available: low, medium, high)
Каталог agy отдаёт идентификаторы с уровнем усилия внутри имени
(gemini-3.7-flash-high, -medium, -low), а в профиле хранится голое имя.
Вызов уходил с пустым --effort, и agy отказывался работать.
Теперь уровень определяется: если он зашит в имени, отделяется от модели;
если нет — берётся из каталога, предпочтительно medium. Проверено, что
claude-sonnet-4-6 при этом не разбирается ошибочно: -6 уровнем не
является.
Отдельно: восемь профилей Antigravity отвечают «Please sign in to view
available models» при HOME=~/.hermes/agy_profiles/<slot>. Это пустые
заготовки без учётных данных, а не поломка: agy запускается с подменённым
HOME ради изоляции аккаунтов, и в незаполненном каталоге ключей нет.
Подписочные аккаунты Codex и Claude: отказ по API-ключу теперь объясняет,
что такие аккаунты подключаются входом по ссылке, а не ключом. Проверка
обращается к каталогу моделей, где платформенному ключу нужны права
api.model.read, и подписочный токен там получает 401 или 403 при исправном
аккаунте.
610 passed, ruff clean, релизный гейт 10/10.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Адрес. Адаптер всегда шёл на 127.0.0.1:11434, получал Connection refused и
помечал аккаунт нерабочим — при том что каталог из девятнадцати облачных
моделей у него получался. У Ollama Cloud локального сервера нет вовсе,
есть только ключ. Теперь при заданном ключе и незаданном адресе адаптер
идёт на ollama.com; явно указанный адрес по-прежнему в приоритете.
Проверено: только ключ даёт https://ollama.com, заданный адрес — его же.
Ключ. Как у NVIDIA и OpenRouter, он читался только из auth_config в
router_profiles.yaml, куда мастер его не кладёт. Добавлено чтение из
хранилища учётных данных.
Лимиты. Провайдер ollama безусловно относился к локальным и получал
подпись «Без ограничений (локальная модель)». Для облачного аккаунта это
неправда: лимиты у него есть. Теперь облачный аккаунт (узнаётся по
наличию ключа) показывает «Н/Д: лимиты не измерены — провайдер не
сообщает их через API».
Отдельно: отказ agy теперь доносит причину. Прежнее «код 1; каталог не
получен» скрывало и текст ошибки, и главное — что agy запускается с HOME,
подменённым на каталог профиля ради изоляции учётных данных. Если вход
делался обычным agy в оболочке, ключи легли в настоящий домашний каталог,
и профиль пуст. Теперь в сообщении и ответ agy, и использованный HOME.
610 passed, ruff clean, релизный гейт 10/10.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Каталог NVIDIA публичный: GET /v1/models отдаётся вообще без ключа и
возвращает одни и те же 83 модели всем. Проверено запросом. Полей о
доступности в нём нет — только id, object, created, owned_by. Значит
узнать из каталога, чем может пользоваться аккаунт, невозможно.
Единственный достоверный способ — спросить у самой модели. Недоступная
отвечает «404 Function ... Not found for account <id>» до генерации, то
есть её проверка ничего не стоит; доступная расходует один токен при
max_tokens=1.
Добавлены модуль model_entitlements, действие probe_account_models и
кнопка «Определить доступные модели» в карточке аккаунта для NVIDIA и
OpenRouter. Запуск только по явному нажатию с подтверждением: опрос
тратит вызовы и упирается в ограничения частоты. Результат сохраняется.
Модели раскладываются на три группы, а не на две: доступные, не выданные
аккаунту и НЕ ОПРЕДЕЛЁННЫЕ с причиной. Отвергнутый ключ (401/403),
превышение частоты (429) и сетевой сбой попадают в третью группу —
выдавать их за «недоступно» нельзя. Проверено: без ключа все модели
получают «ключ не задан», с неверным ключом — «ключ отвергнут (HTTP 403)».
610 passed, ruff clean, релизный гейт 10/10.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
При проверке подключения ключ читался из window._wiz_token. Переменная
переживает предыдущие попытки подключения и могла оказаться пустой или от
другого провайдера: OpenRouter отвечал «HTTP 401: Missing Authentication
header» при заполненном поле, и причина была не видна.
Прямой вызов validate_connection с ключом даёт «401: User not found», то
есть заголовок формируется верно — дело было в источнике значения.
Теперь ключ и адрес читаются из полей в момент проверки.
609 passed, ruff clean.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Установщик копировал файлы, но работающий сервер не трогал. Процесс
продолжал выполнять прежний код из памяти, и владелец видел старый
интерфейс при новом номере сборки: раздача статики читает файлы с диска,
а вся логика действий живёт в загруженном модуле. Три сборки подряд
ставились в файлы, но не в работу — отсюда «сброс не работает»,
«очистка не работает», «версия не изменилась».
Добавлен шаг 0: остановка процессов хаба до копирования. Ищутся только
процессы текущего пользователя и только по признакам хаба
(antigravity_provider.router.web, hermes_hub_web_entry). Сначала обычное
завершение, десять секунд ожидания, затем принудительное. Если процессы
всё же остались, установка НЕ прерывается — файлы обновляются, а владельцу
сообщается, что старый код продолжит работать, пока он их не снимет.
В итоговом сообщении сказано, что хаб остановлен и его надо запустить
заново, и дана команда для проверки, что поднялся новый код.
609 passed, ruff clean.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
agy. Поиск проверял только раскладку Windows (%LOCALAPPDATA%/agy/bin) и
PATH. В Linux утилита ставится в ~/.local/bin, а хаб запускается с
урезанным окружением, где этого каталога в PATH нет. Владелец видел «agy
executable not found» при установленной и работающей утилите — which agy
находил её по адресу /home/ochenstarik/.local/bin/agy.
Добавлены стандартные места Linux: ~/.local/bin, /usr/local/bin, /usr/bin,
/snap/bin. Сообщение об отказе перечисляет проверенные пути. Отдельно
различается «нет доступа к каталогу» и «файла нет».
Ollama Cloud. Мастер требовал адрес локального сервера, которого у
облачного аккаунта не существует: там только ключ. Теперь при заданном
ключе недоступность локального адреса не считается отказом — аккаунт
проверяется по каталогу ollama.com и принимается с честной оговоркой,
что локальные модели недоступны. Проверено: 19 моделей каталога.
Без ключа поведение прежнее.
609 passed, ruff clean, релизный гейт 10/10.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Ключ API. add_account сохраняет его через ProfileAuthManager, а адаптеры
NVIDIA и OpenRouter читали только profile.auth_config из
router_profiles.yaml — туда ключ не попадает. Запрос уходил без заголовка
Authorization, и провайдер отвечал «401: Header of type authorization was
missing», хотя список моделей тем же ключом получался: обнаружение читает
ключ из хранилища, а адаптер читал из конфигурации.
Проверено: auth_config в yaml пуст, адаптер теперь находит ключ в
хранилище учётных данных.
Счётчик аккаунтов. Значок в меню брал readiness.accounts_connected_count
(строго AUTHENTICATED), а карточка на странице считала профили правилом
«не NOT_CONFIGURED». Владелец видел 9 в меню и 3 на странице. Приведено к
одному определению — тому же, что у страницы.
609 passed, ruff clean, релизный гейт 10/10.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
A48 и A49 добавили workspace.js, workflow.js и workflow.css, но заголовки
Cache-Control им не прописали. Проверено запросом к серверу владельца:
у /, /app.js и /style.css стоит no-cache, must-revalidate, а у
/workspace.js и /workflow.js заголовков кэша нет вовсе.
Из-за этого браузер держал старые файлы после обновления сборки, и
владелец видел прежний интерфейс при новом номере сборки — та самая
поломка, ради которой запрет кэша когда-то вводился для app.js.
Перечисление файлов сделано общим списком, чтобы следующий добавленный
файл не оказался снова без заголовков.
609 passed, ruff clean.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Проверялся ровно один путь — $HERMES_HOME/config.yaml. Hermes хранит
конфигурацию по-разному в зависимости от версии и способа установки, и у
владельца на Linux хаб писал «В Hermes: конфигурация не найдена» при
работающем Hermes v0.20.6.
Теперь проверяются семь известных мест, и в сообщении перечисляется, где
именно искали, — владелец видит список и может назвать верный путь.
Отдельно различаются «файла нет» и «нет доступа к каталогу»: закрытый
правами каталог помечается как непроверенный, а не как отсутствующий.
Это тот же класс ошибки, на котором я сам дважды ошибся в диагнозе,
приняв отказ в доступе за отсутствие файла.
599 passed, ruff clean, релизный гейт 10/10.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Кэш опознания. _identities и _snapshots в AccountQuotaService живут в
памяти по ключу «провайдер:слот» и при удалении ключа не чистились.
Владелец удалил аккаунт Antigravity, завёл victor.trushenko@gmail.com, а
в списке остался прежний trushenko.semya@gmail.com. Добавлен
forget_profile, вызывается при удалении учётных данных и при перезаписи
слота другим аккаунтом. Проверено: после сброса запись исчезает.
NVIDIA. Проверка подключения получала каталог моделей успешно — то есть
ключ рабочий и аккаунт опознан, — а затем делала пробный запрос к первой
чат-модели каталога. Каталог NVIDIA общий, доступ к конкретной модели
даётся по аккаунту, и ответ «Function ... Not found for account <id>»
объявлялся провалом подключения. Теперь успешный список моделей считается
доказательством работоспособности ключа, а неудачная проба возвращается
примечанием с предложением выбрать доступную модель.
401 и 403 при этом по-прежнему означают отказ: ключ отвергнут. Различие
поймал тест A54 на неверном ключе.
Выход из программы. Неудачная остановка процессов отменяла выход целиком:
владелец нажимал «закрыть» и оставался в работающем приложении. Теперь
показывается предупреждение, а программа закрывается.
599 passed, ruff clean, релизный гейт 10/10.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Версия. В index.html номер стоял руками в двух местах, а в app.js был
запасным значением '0.1.1'. Номер сборки приходил из API и обновлялся,
версия — нет: владелец обновился до e6eab12 и увидел v0.1.1. Подъём
версии в пяти местах бэкенда до экрана не доходил вовсе. Теперь версия
берётся только из API; если сервер её не передал, пишется Н/Д с причиной,
а не правдоподобный номер.
Установка. Отказ остановки прежнего хаба прерывал установку целиком, и
владелец получал голый код 15. Теперь неудачная остановка не отменяет
установку: причина показывается, работа продолжается, и если файл
действительно занят, об этом скажет копирование с именем файла.
Копирование файлов получило повтор: процесс мог не успеть отпустить файл
после остановки. Пять попыток с паузой вместо отказа с первой.
599 passed, ruff clean.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Владелец просил менять версию сборки. Прежнее указание держать 0.1.1
отменено этим.
Поднято во всех пяти местах, где версия объявлена: version.py,
pyproject.toml, HermesHubSetup.cs, install-linux.sh и
config/compatibility.json. Последний ловится тестом на совпадение с
__version__ — без него прогон падал.
599 passed, ruff clean, релизный гейт 10/10.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Проверено ревьюером исполнением:
check_account больше не отказывает из-за незапущенной фоновой службы.
Раньше кнопка «Проверить подключение» перекладывала работу на службу и
возвращала «Фоновая служба проверки не запущена». Теперь выполняется
настоящий запрос, и владелец видит причину провайдера: «Не удалось
подключиться к локальному серверу LLM».
Удаление ключа больше не ждёт полного обхода провайдеров: замерено 0,0 с
против прежних тридцати.
Пустой ключ отклоняется ДО создания слота — профилей-пустышек не остаётся.
Действие clear_accounts: предпросмотр целей, подтверждение, и Antigravity
защищён — проверено, ключ ag-1 после очистки цел, в цели не попадает.
Лаунчер: значок в области уведомлений, при закрытии окна вопрос «Закрыть
Hermes Hub полностью?», выход снимает браузер и останавливает
собственный процесс сервера (StopOwnedRuntime).
Правка ревьюера: поддержка провайдера проверяется ДО требования ключа.
A54 поставил проверку ключа первой, и у неподдерживаемого провайдера
выводилось «не указан API-ключ» вместо «не поддерживается» — ключ там не
поможет, и сообщение уводило не туда. Тест A42 это поймал.
599 passed, ruff clean, релизный гейт 10/10 на обеих конфигурациях.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Хаб — оконное приложение без консоли, поэтому каждый запуск консольного
exe открывал отдельное окно. Пока проверка аккаунтов шла по нажатию, это
было незаметно. A50 сделал проверку автоматической раз в минуту, и окна
agy.exe стали появляться постоянно, мешая работе.
Добавлен hidden_process_kwargs(): CREATE_NO_WINDOW плюс STARTUPINFO с
SW_HIDE, на не-Windows пусто. Применён ко всем ФОНОВЫМ вызовам:
опрос моделей agy, выполнение запроса agy, чтение ключей, codex_oauth,
launcher_bootstrap, git rev-parse и проверка версии в обновлении.
Вызовы, где окно нужно видимым, не тронуты: вход по OAuth сознательно
использует CREATE_NEW_CONSOLE, запуск установщика тоже должен быть виден.
574 passed, ruff clean.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Сравнение сборок шло только на равенство коммитов: любое расхождение
объявлялось обновлением. Последний релиз на GitHub указывал на 380c218
от 30 августа, у владельца стояла сборка новее — хаб предложил «обновиться»
на старый коммит, владелец нажал и получил откат.
Добавлена проверка по времени: если релиз опубликован раньше, чем
установлена текущая сборка (deployed_at из deployment_manifest.json),
это откат, а не обновление. Такой релиз не предлагается, и владельцу
пишется, что установлена сборка новее опубликованного релиза.
Проверено на фактических данных владельца: релиз 380c218 от 2026-08-30
против установки от 2026-08-31 даёт update_available=False.
Версия остаётся 0.1.1 намеренно, сборки различаются коммитом.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Проверено ревьюером исполнением: подключение с выбором роли кладёт
аккаунт в её цепочку (developer-1 → ['openrouter-1']). Раньше выбор роли
до цепочки не доходил, и ни один аккаунт владельца ни в одной цепочке не
состоял.
Роль на карточке берётся из живой цепочки: role_assignments.get(pid, [])
вместо подстановки догадки. Таблица DEFAULT_SLOT_ROLES удалена — ссылок
на неё в дереве не осталось.
На маршрутизации показывается «Сейчас ответит: <аккаунт>» либо «никто»
с причиной. В плагине Hermes учитываются вызовы мимо хаба
(record_bypass в четырёх местах, счётчик bypassed_calls_count).
Обратное чтение сделано честно: get_hermes_config_status читает
конфигурацию Hermes ТОЛЬКО на чтение, с отдельным тестом на это.
Разрешение конфликтов с A49 и A50 (ветка A51 отведена от 17b368a):
- action_handler: взят вариант A50 — он надмножество, содержит и
назначение в роль, и защиту слота от чужого провайдера;
- auto_assigner: взят вариант A51 — таблица-догадка удалена;
подпись llama.cpp от A50 сохранена, она вне конфликта;
- settings_service, unified_health, app.js: обе стороны, правки
дополняют друг друга;
- тест свежести памяти: взята строгая проверка, вариант A51 молча
пропускал отсутствие записанного коммита.
Две правки ревьюера по итогам слияния:
- в app.js при сложении потерялась закрывающая скобка блока настроек;
- в add_account сохранение учётных данных вызывалось дважды, и второй
вызов обращался к auth_data, которой у уже авторизованного аккаунта не
существует. Перевод аккаунта в другую роль падал с ошибкой, хотя ключ
вводить не требуется. Дубль убран, оба теста A51 проходят.
554 passed, 1 skipped, ruff clean, релизный гейт 10/10 и на конфигурации
владельца, и на пустой.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Проверено ревьюером исполнением, все восемь замечаний владельца закрыты:
Чужой слот. Раньше add_account с profile_id=ag-w1 для nvidia возвращал
ok=True и клал аккаунт в слот Antigravity. Теперь отказ с причиной:
«Слот ag-w1 не принадлежит провайдеру nvidia». Свой слот принимается,
идентификатор выдаётся верный.
Группировка по провайдерам восстановлена: скрытие заголовков групп,
добавленное в A48 (display:contents + display:none), убрано.
Автоматическая проверка запускается при старте веб-сервера
(_start_background_refresh), состояние и списки моделей больше не ждут
ручного нажатия.
Облачные модели Ollama: эндпоинт не выдуман — ревьюер проверил запросом,
https://ollama.com/api/tags отвечает 200 и отдаёт 19 моделей
(gpt-oss:20b, kimi-k2.6, glm-5.1 и другие).
Локальный провайдер подписан llama.cpp вместо «Локальный сервер».
Показ хода при долгом опросе честный: «может занять до минуты на этап».
Конфликты с A49 разрешены сложением: правки дополняют друг друга —
поле пути к хранилищу Obsidian и поле периода проверки аккаунтов,
стили вкладки скиллов и стили групп провайдеров. В тесте свежести памяти
взят вариант A50: коммит извлекается из файла, а не зашит.
545 passed, 1 skipped, ruff clean, релизный гейт 10/10 и на конфигурации
владельца, и на пустой.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
A44: отчёт A40 приведён в порядок. Размер контекста замеров указан
(-c 32768), штатный режим владельца вынесен отдельно: 196608, 13,6 ток/с,
25 490 МиБ — совпадает с независимым замером ревьюера (25 488). Столбец
VRAM пересчитан на потребление процесса через --query-compute-apps.
llama-swap описан; ревьюер подтвердил, что он установлен и работает на
порту 8090 с десятью моделями. Огрызок nemotron убран.
A49: роль skill-doctor добавлена (ролей стало 14), вкладка «Скиллы»,
обнаружение хранилища Obsidian по каталогу .obsidian с проверкой доступа
на запись.
Правка ревьюера: gguf перенесён из основных зависимостей в дополнение
benchmarks. Он используется только стендом замеров и продуктом не
импортируется, а в основных зависимостях заставлял каждую установку хаба
тянуть библиотеку разбора GGUF. Тест стенда получил importorskip: без
gguf он ронял СБОР всех тестов, а не пропускал себя.
526 passed, 1 skipped, ruff clean, релизный гейт 10/10 и на конфигурации
владельца, и на пустой.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
A48 (Codex): вёрстка по макетам. style.css изменён на 178 строк, добавлен
workspace.js, приложены 79 скриншотов — до, после, макеты для сверки.
Проверено глазами: логотип, шапка с поиском, панель инструментов холста,
карточки узлов с моделью и аккаунтом, читаемые подписи связей, инспектор с
вкладками, три карточки внизу. Выдуманных чисел из макета нет, пустые
состояния честные.
A47: хранилище AI-Memory под git локально, память проекта приведена к
действительности, worklog заполнен, составлен перечень агентов сервера.
A45: три кандидата замерены. Файлы и размеры сверены по диску, контрольная
сумма Qwen3-Coder пересчитана независимо и совпала. Скорость 109,6 ток/с
на 64К независимо НЕ перемерена: свободно 1,9 ГБ видеопамяти, проверка
потребовала бы остановить рабочий кодер владельца.
A42: подключение OpenRouter, NVIDIA и Ollama, отдельные ветки обнаружения
моделей, отказ с причиной вместо мнимого успеха.
Конфликт A42 с A41 разрешён в пользу A41: ветка A42 отведена от e7194d3,
до слияния A41 в main, поэтому в ней не было динамических префиксов.
Сохранены _get_prefix и генерация слотов; зашитый provider_slots не взят.
Расширенный список ролей openrouter из A42 принят.
Ожидание теста A42 поправлено: nvidia и nvidia-nim — псевдонимы одного
провайдера с одним адаптером, слоты у них общие.
517 passed, ruff clean, релизный гейт 10/10 и на конфигурации владельца,
и на пустой.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
План моста (AI-Memory/00_SYSTEM/AGENTS_BRIDGE_PLAN.md) числит hermes-hub
единственным репозиторием без корневого AGENTS.md. Существующий
agents/AGENTS.md на AI-Memory не ссылается вовсе.
Мост указывает, где лежит память проекта, что прочитать перед работой и
что обновить после, не дублируя уроки и решения.
Отдельно оговорено, что память доступна только на сервере: агент на
другой машине её не видит и обязан назвать это в отчёте.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
- Measure Qwen3-Coder-30B-A3B, Qwen2.5-Coder-32B, and Tiel-Coder-35B-A3B at 64k and 32k context
- Verify long-context degradation profile and MoE attention scaling
- Add benchmark suite and results to BENCHMARK_MOE_CANDIDATES.md and benchmark_moe_results.json
- Столбец VRAM пересчитан на чистое потребление процессов (nvidia-smi --query-compute-apps)
- В условиях измерений явно зафиксирован контекст 32k токенов и сопоставлен со штатным режимом 192k (13.6 ток/с)
- Добавлен раздел по соответствию 64k порогу Hermes (Qwen3.8-27B, Qwen2.5-Coder-14B, Granite-4.2-8B)
- Проведены живые замеры одновременного размещения: 3 модели помещаются в 31 120 MiB (95% VRAM), 4 модели вызывают CUDA OOM (38 526 MiB)
- Установлен и настроен llama-swap на порту 8090 с автоматической выгрузкой VRAM по TTL
- Удалён незавершённый файл nemotron-3.5-30b
1. P0-1: В action_handler.py в add_account реализовано реальное сохранение профилей
и учетных данных для openrouter, nvidia, nvidia-nim, ollama, local, claude, opencode-go.
Для некорректных действий возвращается честная ошибка ok: False вместо мнимого успеха.
В AutoAssigner добавлены слоты и возможности для openrouter и nvidia.
2. P0-2: В ModelDiscoveryService убраны зашитые списки PID. Добавлены ветки
openrouter, nvidia и выделенная ветка ollama (/api/tags и /v1/models). Ошибки серверов
сохраняются и передаются в интерфейс.
3. P0-3: В unified_health.py разделены статусы временного отката ошибки (STATUS_COOLDOWN)
и исчерпания квоты (STATUS_QUOTA_EXHAUSTED). RATE_LIMITED проверяется до кулдаунов.
В health_tracker.py исключена пометка всего аккаунта при пустом model_name.
В codex_adapter.py уточнена классификация ошибок.
4. tests/test_a42_provider_connect.py: 15 тестов, 500 passed, ruff чисто.
Экран маршрутизации брал профили из currentSnapshot.profiles, тогда как
/api/snapshot отдаёт dataclasses.asdict(HubSnapshot), где поле называется
all_profiles. Ключа profiles в ответе нет — проверено перечислением полей
датакласса. Это было единственное такое место в файле: остальные девять
обращений уже читали all_profiles.
Следствия, которые чинятся разом:
- правая колонка «доступные аккаунты» была всегда пуста;
- «0 аккаунтов» оставалось литералом из разметки, счётчик не переписывался;
- строки цепочек получали пустой профиль, provider становился 'unknown',
и все аккаунты рисовались одной иконкой-заглушкой.
Для строк цепочек берётся полный список профилей, для колонки «доступные» —
только подключённые (правило A26).
Убрана полоса квоты с зашитым width:80%: она была одинаковой у всех
аккаунтов и ни на чём не основана. Вместо неё индикатор измеренного
состояния; неизвестное состояние остаётся серым, а не выдаёт себя за
здоровое.
Подписи связей на холсте центрируются (text-anchor отсутствовал, поэтому
подпись уходила вправо от середины связи и обрезалась о край холста) и
получают обводку, чтобы читаться поверх линии.
Инспектор агента показывал модели из preferred_models — это настроенный
список предпочтений профиля, а не то, что даёт провайдер; model_states
строится перебором того же preferred_models, поэтому запасная ветка давала
тот же набор. Источником стал discovered_models провайдера. Настроенная у
агента модель теперь всегда присутствует в списке: раньше, если её там не
было, ни один option не получал selected, показывался первый вариант, и
сохранение конфигурации молча подменяло модель агента.
486 passed, ruff clean.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
1. P0-1: get_default_router_config() возвращает чистую конфигурацию (0 профилей,
13 канонических ролей с пустыми цепочками). Миграция не внедряет фиктивные профили.
2. P0-2: Учетные данные (~/.hermes/*_profiles/, hub_settings.json) изолированы и
никогда не затрагиваются при сбросе или установке.
3. P0-3: Профили создаются динамически при подключении аккаунтов (ag-1, codex-1, etc.).
Пустые цепочки ролей являются нормальным рабочим состоянием.
4. P0-4: Добавлен экшен reset_router_config и кнопка «Начать настройку заново»
в настройках с подтверждением и созданием бэкапа router_profiles.yaml.bak_<timestamp>.
5. P0-5: scripts/verify_multi_provider_router.py адаптирован и проходит 10/10 PASS
как на пустой конфигурации, так и на заполненной.
6. tests/test_a41_clean_install.py: 6 тестов, 490 passed, ruff чисто.
Установка на Windows упала с кодом 12, и причина была не в новом коде, а в
накопленном состоянии: конфигурация тащила роль под старым именем, роли без
цепочек и 24 пустых заготовки, переживших несколько переименований. Проверка
споткнулась о наследие.
Владелец сформулировал вывод: при первой установке всё должно начинаться с
нуля — сначала аккаунты, потом распределение по агентам.
Задание разводит два случая: конфигурации нет — первая установка, профилей не
создаётся вовсе; конфигурация есть — обновление, ничего не трогается. Плюс
явная кнопка сброса с подтверждением и резервной копией.
Отдельным пунктом, из-за цены ошибки: сброс касается только маршрутизации.
Каталог agy_profiles не затрагивается ни при каких условиях — потеря учётных
данных означает повторный ручной вход в два десятка аккаунтов, включая
Antigravity со входом по ссылке для каждого профиля.
Требуется также прогнать проверочный скрипт установщика на ПУСТОЙ
конфигурации: он этого случая никогда не видел, у него всегда было 24
профиля, и падение на чистой машине дало бы тот же код 12 новому
пользователю.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Владелец получил «Ошибка установки (Код: 12)». Код 12 — провал скрипта
scripts/verify_multi_provider_router.py, который виндовый установщик запускает
после развёртывания. На Linux он не запускается, поэтому там всё вставало.
Скрипт пережил три изменения продукта и не был под них обновлён:
1. Требовал роль "orchestrator". A28 переименовал её в "manager", и проверка
падала на первом же шаге. Теперь актуальное имя спрашивается у реестра
ролей, а не помнится в скрипте.
2. Требовал непустую цепочку у КАЖДОЙ роли. A28 добавил роли, объявленные без
реализации — guardian и cost-controller, — у них аккаунтов ещё нет.
Установка падала из-за роли, которой никто не пользуется. Теперь пустая
цепочка допустима и лишь отмечается, а обязательна она только у
оркестрирующей роли: без неё маршрутизация действительно не работает.
3. Зашивал порядок цепочки codex -> antigravity -> opengo-3 и конкретные
идентификаторы профилей. Но порядок — выбор владельца, он меняет его мышью,
и любая перестановка роняла установку. Проверка переписана на механизм:
берётся настоящая цепочка, роняются все профили кроме последнего
достижимого, и проверяется, что маршрутизатор дошёл именно до него.
Учтён предел max_failover_attempts — за него цепочка не проходится.
Карта адаптеров дополнена claude, grok и local: раньше в ней были только три
провайдера, и хвост цепочки из остальных не покрывался.
Проверено на конфигурации владельца: 10/10 CHECKS PASSED, код возврата 0.
486 passed, ruff чисто.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
- Все строки отчёта BENCHMARK_REPORT.md содержат реальный путь, размер файла по os.stat, sha256 первых 64M и имя из general.name
- Отозваны все гипотетические оценки A38; проведены реальные замеры Phi-4 (83.3%, 58.5 ток/с), DeepSeek-Coder-V2 (75.0%, 61.5 ток/с), Granite-4.2-8B (66.7%, 80.6 ток/с)
- Описано падение скорости Multi-head Latent Attention (MLA) DeepSeek на 32k контексте до 3.35 ток/с
- Восстановлены штатные службы владельца на портах 8081 (Qwen3.8-27B) и 8082 (Qwen3-4B)