Владелец просил менять версию сборки. Прежнее указание держать 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)
Нормализация в сборщике делалась через sed -i, а на Windows в Git Bash это
ненадёжно. Замена на Python сначала не сработала по неожиданной причине:
python3 здесь — заглушка Microsoft Store, которая печатает "Python" и не
выполняет ничего. Нормализация тихо становилась пустой операцией, а сборщик
при этом рапортовал об успехе.
Теперь интерпретатор выбирается проверкой: python3, python, py — берётся
первый, который действительно исполняет код. Не нашёлся ни один — сборка
прерывается с объяснением, потому что установщик без нормализации ломается
на Linux с "$'\r': command not found".
Проверено побайтово на собранной поставке: в прологе ноль байтов 0d, маркер
на своём месте, среди .sh и .py файлов поставки ни одного с возвратом
каретки.
Отдельно отмечу для истории: тревога о возврате CRLF была поднята по ошибке
измерения — grep -c с шаблоном возврата каретки в этой оболочке давал ложные
срабатывания. Сама поставка была исправна и до правки.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Брендбук требует три темы, причём Medium — отдельная, а не осветлённая Dark:
тёмно-зелёный холст #1A2A1F со светлыми кремовыми карточками #F7F1E3.
В style.css она описана целиком и отрисовывается верно — проверено
подстановкой data-theme вручную. Но в список выбора не попала, а applyTheme
знал только light и dark, поэтому любое другое значение сбрасывало тему в
системную. Выбрать Medium было невозможно.
После правки все три переключаются:
dark фон #101510 карточки #1A2A1F
medium фон #1A2A1F карточки #F7F1E3
light фон #F7F1E3 карточки белые
Цвета совпадают с палитрой брендбука. 486 passed, ruff чисто.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
На машине владельца задана ANTHROPIC_BASE_URL=https://api.anthropic.com, и
из-за неё test_claude_health_check_real_probe падал: адаптер строил
".../models" вместо ".../v1/models". Код при этом верен — подставлялось
значение из окружения вместо умолчания.
Проверено: со снятой переменной 486 passed, с заданной — одно падение.
Набор обязан давать одинаковый результат на любой машине; это то же
требование герметичности, ради которого делался A33.
Добавлена автоматическая фикстура, убирающая восемь переменных *_BASE_URL на
время каждого теста. После правки прогон с заданной переменной даёт
486 passed.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Отчёт benchmarks/BENCHMARK_REPORT.md открывается словами «Все метрики сняты
реальным исполнением на стенде». Для трёх строк из семи это неправда.
Сверено с диском сервера. Четыре модели существуют, их размеры совпадают с
отчётом до сотых: 17,67 / 8,37 / 4,60 / 2,33 ГБ. Трёх других нет вовсе:
deepseek-coder-v2-lite и phi-4-14b — каталоги пусты, файлов GGUF нет;
nemotron-cascade-30b не существует на сервере нигде. При этом в
benchmark_results.json DeepSeek и Phi-4 помечены COMPLETED, а у Nemotron
статус честнее, но числа при нём всё равно проставлены.
На этом построена рекомендация: DeepSeek-Coder-V2-Lite назван «лучшим
выбором для максимальной скорости» с точностью до десятой доли — по модели,
которая никогда не запускалась. Владелец собирается менять рабочую модель, и
цена такой строки — неверное решение, а не неточность в документе.
Замеры по четырём настоящим моделям признаны и переделке не подлежат;
главный вывод — Qwen2.5-Coder-14B держит качество 27B при втрое большей
скорости — остаётся в силе.
Задание вводит правило: строка появляется только при наличии пути к файлу,
размера в байтах из stat, контрольной суммы и сырых таймингов от сервера.
Модель не скачалась — раздел «не проверено» с причиной, это принимается.
Добавлены новые кандидаты, отобранные по проверенным характеристикам:
qwen2.5-coder-32b, granite4.2:8b (на диске лежит 3.2-preview, а не релиз),
nemotron-3.5-lightning, laguna-xs-2.1, lfm2.5. Отклонены с проверкой:
gpt-oss:120b (80 ГБ), Qwen3.8-Flash-Next (125B/6B, ужатое IQ1_S — 72,5 ГБ).
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
1. OllamaAdapter: поддержка локального инстанса по умолчанию и удаленного Ollama API
с кастомным base_url и опциональным Bearer токеном, discovery по /v1/models и /api/tags,
статус квоты «Без ограничений».
2. ClaudeAdapter: реальный health_check API probe и динамический discover_models.
3. OpenRouterAdapter: обязательные заголовки HTTP-Referer и X-OpenRouter-Title,
сбор метаданных моделей (context_length, display_name).
4. NvidiaAdapter: парсинг заголовка Retry-After и динамическая задержка при 429.
5. Экспорт лимитов: эндпоинт GET /api/quotas/export (JSON / CSV), экшен export_quotas
и кнопка выгрузки в веб-интерфейсе с маскированием секретов.
6. tests/test_api_providers_a32.py: 15 тестов, 434 passed, ruff чисто.
1. P0-1: Восстановлены все веб-обработчики в app.js (openAddAccountWizard,
handleNodeAccountChange, handleNodeModelChange, handleRefreshProviderModels,
checkUpdates), связаны с потоками startDeviceAuth и startRedirectAuth,
выбор слота обязателен и понятен пользователю.
2. P0-2: Добавлены адаптеры OpenRouter и NVIDIA NIM с поддержкой динамического
base_url, множественных аккаунтов без ограничений, GET /models и честным
отображением квот/«Н/Д».
3. P0-3: В мастере подключения локального провайдера реализована кнопка автопоиска
(discover_local_models), отображение серверов, ошибок портов и автозаполнение.
4. P0-4: Полностью удален устаревший десктопный интерфейс CustomTkinter
(router/ui/** 20 файлов, hermes_hub_app.py), зависимости customtkinter и pillow
убраны из pyproject.toml и инсталляторов, оставлен единый ярлык «Hermes Hub».
5. 418 passed, 1 skipped, 4 deselected, ruff чисто.
Реализована поддержка произвольных параметров запроса (request_options) для
локальных профилей:
1. RouterProfileConfig и ProfileViewModel дополнены полем request_options
с полной поддержкой вложенных словарей и сериализацией в YAML.
2. LocalLLMAdapter подмешивает request_options в тело POST /chat/completions.
Явные поля запроса имеют приоритет, при расхождениях пишется warning.
Изоляция провайдеров сохранена: другие адаптеры не трогают request_options.
3. Неизвестные/некорректные параметры обрабатываются чисто и классифицируются
как INVALID_REQUEST с извлечением текста ошибки сервера.
4. В веб-интерфейсе реализована карточка профиля с редактором JSON параметров,
живой валидацией синтаксиса, предпросмотром тела запроса и проверкой
подключения.
5. Отсутствие зашитых параметров enable_thinking и reasoning_effort в логике.
6. 526 passed, 3 skipped, 4 deselected, ruff чисто.
Локальная модель не закрывает задачи: Hermes сообщает о таймауте 180 секунд
после четырёх вызовов и переключается на другого провайдера.
Причина измерена, а не предположена. Служба запущена с --reasoning on и
--reasoning-budget 4096, а модель выдаёт 13,4 токена в секунду. На простой
задаче рефакторинга с лимитом 1500 токенов получено:
сгенерировано 1500 токенов за 111,6 с
рассуждений 5483 символа
ответа 0 символов
Весь лимит уходит на размышления, до ответа модель не доходит. За 180 секунд
она успевает около 2400 токенов — и это тоже одни рассуждения.
Отключение рассуждений на уровне запроса проверено и работает:
reasoning_effort: none ответ за 19,5 с
chat_template_kwargs: enable_thinking=false ответ за 11,4 с
Одиннадцать секунд вместо ста одиннадцати. Серверный флаг --reasoning off
решил бы это грубо, лишив рассуждений насовсем, поэтому владелец выбрал
гибкий путь: параметры задаёт хаб, по профилю.
Задание требует не зашивать ни enable_thinking, ни reasoning_effort: набор
ключей зависит от версии llama.cpp, и это данные конфигурации, а не
константы кода. Отдельным критерием — проверка живым запросом к серверу
владельца с замером времени до и после.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Проверка A37 исполнением. Защита границы рабочей области работает, обходы
через ../ и ~ в validate_path отсекаются, каталоги учётных данных закрыты.
Но в validate_command нашлась дыра ровно в том месте, ради которого guard и
делался.
Тильда и переменные окружения не раскрывались перед проверкой. Путь
"~/.hermes/agy_profiles" не считался абсолютным, склеивался с каталогом
проекта в путь с буквальным "~" внутри и признавался допустимым. Измерено:
rm -rf ~/.hermes/agy_profiles РАЗРЕШЕНО
rm -rf ~/.ssh РАЗРЕШЕНО
rm -rf $HOME/.hermes РАЗРЕШЕНО
тот же путь абсолютным отказ
тот же путь через validate_path отказ
То есть самый естественный способ написать опасную команду обходил защиту, а
абсолютный путь — нет. После правки все три отклоняются, штатное удаление
внутри проекта по-прежнему разрешено.
Добавлен тест, удерживающий это свойство.
Проверено отдельно, что защита не ломает продукт: страница, app.js, health и
snapshot отдают 200, в снапшоте 13 ролей, секретов в ответе нет, проверка
обновлений работает. 508 passed, ruff чисто.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>