История до 1.7.5 — в git log (сообщения коммитов вида v1.7.5: …). Здесь ведём план и состав следующего релиза.
Статус: собрано 05.11.2026.
Дефект: чип ailab-limit в сборке 1.7.6 всегда показывал AI !, в desktop.log каждые 60 с [ailab-limit] fetch failed: ключ шлюза не найден в .env.
Причина: gw_usage.py искал ключ только как литерал sk-… в .env, а с 1.7.6 ключа в .env нет вообще — доступ выдаётся через key_cmd (gw-key.ps1, короткоживущий токен as-…).
Что изменено — только desktop-plugins/ailab-limit/gw_usage.py:
| Что | Как |
|---|---|
key_cmds() |
читает key_cmd из config.yaml профилей, активный первым |
token_from_cmd() |
запускает команду (CREATE_NO_WINDOW), берёт access_token из JSON; 12 с на попытку, не больше двух попыток в бюджете 20 с, по таймауту гасится всё дерево процессов (taskkill /T) — иначе shell.exec рвётся на своих 30 с |
key_candidates() |
сначала sk-… из .env (совместимость со старыми установками), затем токен key_cmd; на 401/403 — следующий кандидат |
| JSON ответа | добавлены total_spend, reset_at, source |
mark() |
последний запуск пишется в %LOCALAPPDATA%\hermes\logs\ailab-limit-last.json — чтобы отличать «чип не запускался» от «данных нет» |
Формат ответа для plugin.js не менялся — плагин не правился.
Проверено: прогон скрипта на живой установке и вызов из рендерера вернули одинаковый ответ — {"ok": true, "spend": 12.06, "budget": 100.0, "duration": "1d", "pct": 12.1, "alias": "testoff", "total_spend": 26.5, "reset_at": "2026-11-06T00:00:00+00:00", "source": "… из .env"}.
Дефект, найденный на тестовом ноутбуке 05.11.2026: ночное обновление 1.7.6 → 1.7.7 прошло «успешно» (в update-check.log — обновление установлено), а папки App на диске не осталось: ярлык на рабочем столе есть, запускать нечего.
Причина: в app-setup.zip на сервере маркер app-version.txt был 1.7.6, а дистрибутив уже 1.7.7. Установщик распаковывал архив поверх рабочей папки, видел несовпадение версий и удалял App целиком — «окно не ставлю». Метка .ailab-install.json при этом уже была записана как 1.7.7, поэтому следующая ночная проверка считала машину актуальной и окно не возвращала.
Что изменено:
| Файл | Было | Стало |
|---|---|---|
setup.ps1, шаг 9/9 |
распаковка поверх App, при несовпадении версии Remove-Item $AppDir |
распаковка в App.new, сверка app-version.txt, подмена папки только при совпадении; при несовпадении рабочее окно остаётся нетронутым |
setup.ps1, финал |
при отсутствии окна метка установки врала | в .ailab-install.json добавляется app_missing: true |
update-check.ps1 |
сравнивал только версии | если нет App\Hermes.exe — ставит заново независимо от версии |
build-app.ps1 |
архив окна мог остаться от прошлой версии | при каждом выпуске архив пересобирается с app-version.txt = версии дистрибутива |
Дополнительно (после жалобы «установлено 1.7.7, а в окне метка 1.7.6»): метка в композере вшивается в рендерер при сборке (vite.config.ts → __AILAB_BUILD__), поэтому правка одного app-version.txt внутри архива её не меняет. Окно пересобрано заново:
AILAB_BUILD=1.7.7 AILAB_DISTRIBUTION=1 npm run build → метка 1.7.7 в dist/assets/main-C7bK8VL0.js, вхождений 1.7.6 в сборке нет;npm run builder -- --dir -c.directories.output=<staging> в отдельный каталог (dist\release-app-177), живой Hermes владельца не тронут;build-app.ps1 -App <staging> — 1216 записей, 179,29 МБ, md5 a63a46e8b20182ccf5d91a5a1e4fc94d, Hermes.exe 246 884 864 б;apps/desktop/assets/icon.ico был старым (101 488 б) и любая свежая сборка подставляла его вместо фирменного — заменён на om-lab.ico (35 450 б).Статус: состав накапливается. Сборка не запускается — Олег добавляет нововведения, релиз собирается после закрытия состава.
Тема: правило периметра данных доводится до работающего механизма, а не остаётся декларацией в промпте.
Основание — аудит профиля ai-si 05.11.2026. Проверенное состояние:
| Что | Факт |
|---|---|
skills/productivity/docx/references/anonymization.md |
процедура полная: тело, таблицы, все headerN/footerN.xml, docProps/core.xml, app.xml, картинки; замена подчёркиваниями; верификация из 3 шагов. Лежит только в профиле ai-si — в сборке файла нет |
skills/productivity/docx/scripts/scan_open_pii.py |
работает (--summary, --header, --json, --fail-on-open). Лежит только в профиле ai-si — в сборке файла нет |
skills/ailab/anonymization-check |
лежит в сборке, в профиль не ставится — категории ailab в установленном профиле нет |
skills/engineering/ai-data-perimeter |
есть только в профиле ai-si, в сборке отсутствует → получателям не приедет |
profiles/<p>/hooks/ |
пусто — принуждения нет |
SOUL.md сборки |
про обезличивание, периметр и заказчика — ни слова |
Отсюда формулировка «у нас обезличивание до отправки в модель» сегодня не подтверждается: правило записано в скилле, но срабатывает по триггеру и зависит от того, вспомнит ли модель его применить.
Проблема: одна и та же процедура описана в трёх местах — docx/references/anonymization.md, ailab/anonymization-check, engineering/ai-data-perimeter. Три редакции разъедутся.
Иерархия, которую фиксируем:
| Место | Роль |
|---|---|
engineering/ai-data-perimeter |
канон: правила периметра, проверка «соблюдается ли», порядок работы с документом заказчика, чек-лист и нормы (references/perimeter-rules.md) |
docx/references/anonymization.md |
техника правки DOCX-пакета + scripts/scan_open_pii.py — исполнение под каноном |
ailab/anonymization-check |
свести к тонкой ссылке на канон либо убрать; сейчас это третья редакция того же правила |
engineering/ai-data-perimeter в сборку (dist/vasily/skills/engineering/) — в сборке этой категории нет вообще, поэтому переносится каталог целиком, вместе с references/perimeter-rules.md и templates/soul-perimeter-block.md.skills/productivity/docx/references/anonymization.md и skills/productivity/docx/scripts/scan_open_pii.py. Оба сейчас есть только в профиле ai-si; в dist/vasily/skills/productivity/docx/ их нет. Копировать без __pycache__.skills/engineering/ai-data-perimeter/ (SKILL.md, references/perimeter-rules.md, references/hermes-intercept.md, templates/soul-perimeter-block.md), skills/productivity/docx/references/anonymization.md, skills/productivity/docx/scripts/scan_open_pii.py. md5 совпадают с профилем ai-si, __pycache__ не попал, синтаксис скрипта проверен.engineering и ailab доезжают до установленного профиля при установке и обновлении (ailab сейчас не доезжает — см. таблицу выше).skills/engineering/ai-data-perimeter/SKILL.md + references/perimeter-rules.md; третьей редакции правила в профиле нет.SOUL.md — сведено с ai-data-perimeterСверено с каноном. SOUL не переписывает светофор и контуры (они в references/perimeter-rules.md), а закрывает то, что действует до первого чтения файла, и не противоречит трём правилам скилла: аудит вместо согласия (Правило 0), декларация ≠ контроль (Правило 1), агент сам канал (Правило 2).
Расхождения первой редакции, исправленные при сверке:
| Было в первой редакции | Что говорит скилл | Исправлено |
|---|---|---|
| не покрывала запрос «подтверди, что соблюдается» | Правило 0: это аудит по двум слоям, а не согласие | добавлены оба слоя и запрет ответа по одному |
| «не открывается, пока не решено» — без статуса данных | документ до классификации считается красным | добавлен статус «считается красным» |
| правило жило само по себе | процедура — в скилле, SOUL задаёт только порядок | добавлена ссылка на канон, дублирование убрано |
| не было стоп-сигнала | «маркеров нет» = скорее всего оригинал; ноль находок ≠ чисто | добавлено |
| не сказано, кто обезличивает | обезличивание — работа человека, спорное агент не решает | добавлено |
Формулировка для вставки в SOUL.md (в сборку и в рабочий профиль):
## Периметр данных Правила, светофор и процедура — скилл `ai-data-perimeter`. Здесь — то, что действует до первого чтения файла. - Документ заказчика (ТЗ, договор, переписка, форма, ИД) до классификации считается красным. Инструментами чтения он не открывается, пока не решено, откуда берётся обезличенная копия: её даёт владелец или агент делает копию и читает только её. - Оригинал не открывать «чтобы посмотреть объект». Таблица соответствия токенов и реальных значений не читается никогда — она и есть закрываемые данные. - Обезличивание — работа человека по его таблице соответствий. Агент делает копию по технике `docx/anonymization.md`, только если об этом попросили, и не решает за человека, что закрывать в спорных случаях. - Проверка полноты: `python scripts/scan_open_pii.py "<файл>" --summary`. Агент читает счётчики; полный отчёт со значениями — человеку, потому что находки и есть закрываемые данные. - «Маркеров нет» — единственный стоп-сигнал: скорее всего, это оригинал. Ноль находок ≠ «документ чист». Формулировка: «шаблоны нашли N, открыто: …». - «Документ обезличен» и «у нас это соблюдается» без проверки файлов — запрещены. На просьбу подтвердить меру проверяются два слоя: организационный (приказ, регламент, журнал) и технический (SOUL, установленные скиллы, hooks). Ответ по одному слою — ошибка в обе стороны. - Если механизма принуждения нет — говорить это прямо: правило в промпте срабатывает по триггеру, а не по факту.
SOUL.md в сборке (приезжает всем получателям) + profiles/<p>/SOUL.md в рабочем профиле (иначе правило не действует у того, кто уже установлен).Проверено 05.11.2026 по установленной сборке: точка перехвата в Hermes существует и врезана в путь запроса.
ctx.register_middleware("llm_request", fn) — hermes_cli/plugins.py:939.agent/turn_api_request.py:156 → apply_llm_request_middleware(...), и api_kwargs заменяются тем, что вернул плагин (hermes_cli/middleware.py:88).request, включая messages/input, и может переписать их или прервать вызов исключением.tool_request (аргументы инструмента до выполнения) и llm_execution (обёртка самого вызова провайдера).Решение Олега 05.11.2026: режим — маскировать и логировать. Блокировка — только там, где маскирование бессмысленно. Причина: шаблоны ПДн дают ложные срабатывания на техническом тексте (номера СП и ГОСТ, ИНН поставщика в спецификации, кадастровые в выписке), а жёсткая блокировка валит нормальную работу и толкает её на личный телефон.
Категория — не по шаблону, а по типу документа и роли. Классификатор угадывает и ошибается; поэтому тип документа определяется до чтения, а подтверждает человек одним вопросом, и ответ живёт в метке сессии.
Три точки в одном плагине:
| Точка | Что делает |
|---|---|
tool_request (read_file, DOCX/PDF-парсер, OCR) |
определяет тип документа по имени файла, титулу и штампу; сверяет с ролью пользователя; ставит метку категории в сессии; при запрете — отказ до чтения файла, содержимое в модель не попадает |
llm_request |
маскирует по правилам категории из метки; пишет строку в журнал |
llm_execution |
возвращает реальные значения в ответе (демаскировка) |
Плагин работает только с содержимым запроса к модели. Ключи доступа, учётные данные и личные файлы он не читает и не меняет — это отдельный контур, к политике доступа к документам отношения не имеет.
Политика — файл в профиле (policy.yaml), правится без пересборки:
| Категория | Кому | Что входит | Режим |
|---|---|---|---|
| 🟢 зелёный | всем | открытые нормативы, методики, обезличенные тексты | без маски |
| 🟡 жёлтый | всем | ТЗ, реестры замечаний, переписка, локальные сметы, ВОР | маска + журнал |
| 🔴 красный | только роль «договорной пул» (несколько человек) | договоры, сводные сметы, акты, ИД, кадры, финансы | остальным — отказ с подсказкой, к кому идти |
Маскировать красное и отправлять нельзя: у договора маска убивает предмет — стороны, суммы, особые условия. Поэтому для красного единственный корректный ответ — не пускать.
Два ограничения по доступу — согласованы с Олегом 05.11.2026:
Источник ролей — Единый докер USER.xlsx (лист USERS, колонка «роль», добавлена 05.11.2026), 11 учёток:
| Роль (как записана) | Кто | Категории | Отправка в облако |
|---|---|---|---|
| инженер ОВиК, ТЗ, ТУ, СТУ, проекты — 7 | Гришкевич, Воробей, Фролагина, Воронцов, Анушов, Кашина, Нечаев | 🟢🟡, ТЗ — всем | да, с маской |
| гл. бухгалтер (договоры, контракты, сметы, тендеры) — 1 | Хундадзе | 🟢🟡 + открытие красного | нет |
| совладелец директор (договоры, контракты, сметы, тендеры) — 2 | Швецов, Дуда | 🟢🟡 + открытие красного | нет |
Требования к колонке, без которых policy.yaml не собрать:
engineer / contracts / admin), а не перечисление задач: «инженер ОВиК, ТЗ, ТУ, СТУ, проекты» не парсится и меняется от задачи к задаче. Описание оставить отдельной колонкой для человека.policy.yaml генерируется из этой таблицы скриптом, а не правится руками: два источника правды разъедутся на первой же кадровой замене.root (TESTOFF) политику не обходят — иначе одна учётка отменяет контроль. Для них отдельная запись в журнале с пометкой admin.engineer — 7, contracts — 3, admin — 1), столбец H «роль — что делает» — исходное описание задач для человека. Дуда В. заведён (ключ выдан сотруднику, хэш в таблицу не сохранялся) — прежний пункт снят.Единый докер USER.xlsx — личная таблица Олега (OneDrive). Источник ролей, но не рабочий файл сборки: агент её читает, не изменяет и не переписывает. policy.yaml генерируется из копии, снятой на момент подготовки релиза, — чтобы правка личной таблицы не ломала политику, а политика не правила личную таблицу.
Критерий приёмки:
Порядок работ: сначала tool_request + политика + журнал в режиме «только запись» (ничего не маскируем, смотрим поток), затем включаем маску на жёлтом, и только после настройки правил — демаскировку ответа.
Единый источник UI- и функциональных требований — C:\Users\Admin\OneDrive\Документы\OM LAB\OM_Lab — требования к следующей сборке.md (§0 — сводка, §1–§4 — тексты). Здесь только состав и статусы; требования не дублируются, при расхождении верен документ.
| № в документе | Пункт | Раздел | Статус на 05.11.2026 |
|---|---|---|---|
| 1 | Только разрешённые модели, персональный ключ, бренд агрегатора скрыт | §1 | готово к реализации |
| 2 | Один провайдер (шлюз), прямой polza.ai удалён, discover_models выключен + миграция для установленных |
§3a | готово к реализации |
| 3 | Прогресс-бар лимита — по ключу в рублях, через шлюз | §1a | блокер: нужен /v1/wallet на шлюзе |
| 4 | Vision через шлюз тем же ключом | §1b | готово к реализации |
| 5 | Резерв proxyapi.ru, только автопереключение, пользователю не виден | §2 | в объёме 1.7.6, делается при сборке (работа шлюза, не клиента): ключ есть (797 моделей), группы и fallbacks заведены, но LiteLLM отклоняет переход по правам ключа → переделать на второй деплоймент в группе + cooldown_time: 300 |
| 6 | Настройки: пять вкладок, «О программе» скрыть | §3 | готово к реализации |
| 6a | Вкладка «Боты» скрыта, Bot Mode остаётся в коде | §3b | решено 04.11.2026: только вкладка |
| 6b | Первое открытие после установки — тёмная тема | §3c | записано 04.11.2026 |
| 7 | Бот на звонок в Телемост | §4 | блокер: отдельная учётка Яндекс |
| 8 | Ключ привязан к машине: зачисление + key_cmd, статического ключа в .env нет |
§1c | сделано 05.11.2026 (см. ниже) |
Пункт 8 (привязка доступа к машине) — сделано 05.11.2026. Шлюз: gw-issuer v2 — /enrol (обмен старого sk-… на зачисление), /app-key (сессия as-… на 300 с по X-Install-Token), /resolve (подмена сессии на ключ человека для nginx), /revoke, /list. nginx на gw.ailabscrume.ru получил auth_request: в LiteLLM уходит тот же ключ, что и раньше, поэтому лимиты и учёт не меняются (короткие ключи через /key/generate отвергнуты — каждый ключ это отдельная строка расхода и прогресс-бар §1a показывал бы ноль). Клиент: gw-key.ps1 (DPAPI-конверты, коды 0/1/2/3/4, кэш при недоступном хабе), установщик зачисляет машину по почте и коду, пишет key_cmd вместо key_env, чистит .env, файл зачисления кладёт в %LOCALAPPDATA%\AILabSCRUME (вне дома агента). Проверено живьём: агент на временном профиле ответил моделью через key_cmd при пустом .env; отзыв зачисления → helper выходит с кодом 3. ISSUER_STRICT=0 — статические ключи пока проходят насквозь; включать 1 после обновления всех машин.
Брендинг 1.7.6 (решение Олега 05.11.2026). Название агента — OM_Lab_Agent (видимые строки: установщик, ярлык, описание задачи Планировщика, README/INSTALL/CHANGELOG, проверка самоназвания $NamePat). Лого на пустом чате — assets/scrum-engineering.svg заменён на логотип OM Lab (прозрачный PNG, обрезка по содержимому + поля 2 %, 640 px внутри SVG, alt OM_Lab_Agent, ширина 280 px, height:auto, objectFit:contain); под логотипом подпись «Приятной работы, с уважением, команда OM_LAB_Co». Иконка — assets/om-lab.ico = image-cutout.ico (256×256, прозрачная) в resources/icon.ico и в ярлыке; иконка самого Hermes.exe не менялась (нужен rcedit, Олег отложил). Номер сборки — метка BuildVersionLabel в chat/composer/controls.tsx: версия дистрибутива вписывается в рендерер при сборке (AILAB_BUILD → __AILAB_BUILD__), appVersion (версия ядра, 0.22.7) не показывается. Рендерер окна лежит в ДВУХ местах — app.asar и app.asar.unpacked\dist; обновлять оба, иначе интерфейс остаётся старым при живой иконке. Сборка окна для получателей идёт через staging-копию (build17.ps1), чтобы не подменять рабочий Hermes владельца. Display-имя Electron НЕ меняем (product-identity.cjs: display: 'Hermes'): оно задаёт %APPDATA%\Hermes, где лежат список чатов и настройки окна — переименование «потеряло» бы их у всех. Триггер автообновления не трогаем (остаётся Daily 03:00 + 0…44 мин с -StartWhenAvailable); поведение при выключенном ПК: обновление выполняется при первом включении/входе, но если в этот момент открыто окно Hermes.exe → «отложено» до следующего окна.
Что реально войдёт в 1.7.6: пункты без внешних блокеров — 1, 2, 4, 6, 6a, 6b, 8 (плюс безопасность п.1–3 выше) и пункт 5 (резерв, работа шлюза — по решению Олега 05.11.2026 переделка делается при сборке 1.7.6, а не сейчас).
proxyapi.ru живой, GET /v1/models → 200, 797 моделей, ограничений на ключе нет (подтверждено Олегом). Группы proxy/<vendor>/<model> и router_settings.fallbacks для всех алиасов уже заведены, но переход не выполняется: в логе gw-llm — Fallback to proxy/google/gemini-4-argon was skipped because the caller is not authorized to call it. Причина: у ключа сотрудника в models только его 9 моделей, proxy/* в них нет. Отсюда решение: не отдельная группа, а второй деплоймент внутри той же группы <alias>/<vendor>/<model> (список моделей у ключа не меняется — резерв остаётся невидимым), плюс явный cooldown_time: 300 (сейчас «5 минут» держатся на дефолте роутера, в router_settings его нет).anthropic/claude-sonnet-5.5 → резерв anthropic/claude-sonnet-5-5 (у proxyapi дефис вместо точки);openai/gpt-6.1-sol-pro → на proxyapi отсутствует, резерв = деградация на openai/gpt-6.1-sol (подтверждено Олегом). Технически это второй деплоймент в той же группе: model_name = <alias>/<vendor>/<model> (то, что видит ключ), litellm_params.model = фактическая модель proxyapi, api_base = https://api.proxyapi.ru/v1. Группа и список моделей у ключа не меняются.testoff/… в названиях моделей, точный перечень вкладок настроек, маппинг моделей proxy.Мероприятия по этапам. Галочка ставится по факту проверки, а не по факту «сделали».
policy.yaml: общий файл на диске (план) или политика на шлюзе; шлюз остаётся вторым рубежом для тех, кто ходит мимо агента.engineering/ai-data-perimeter в сборке и в установленном профиле (п.1). В сборке лежит с 05.11.2026; осталась проверка установленного профиля после чистой установки.SOUL.md сборки и профиля (п.2).tool_request + policy.yaml + журнал в режиме «только запись», без маски (п.4).llm_execution.CHANGELOG.md: перенести 1.7.6 из «план» в «выпущено», поставить дату и номер сборки.README.md: раздел безопасности — новые правила и роли.distribution.yaml → version: 1.7.6; README.md → «Текущая версия: 1.7.6».v1.7.6: <главное изменение> + тег.config.yaml) — маска не должна требовать релиза, чтобы её отключить при массовых ложных срабатываниях.Статус: под вопросом. Собирать только после явного «да» Олега при сборке.
Тема: один ключ сотрудника (sk-…) открывает модели и MCP ГОСТ-ассистента; прямой api.gostassistent.com из клиента убирается.
Основание: ТЗ §7 («MCP гостассиента https://gw.ailabscrume.ru/mcp тем же ключом») не доехало в 1.7.6/1.7.7 — клиент ходит напрямую с отдельным upstream-ключом 64 символа. Проверено живьём 06.11.2026: ключ сотрудника sk-… (25 симв.) на шлюзе gw.ailabscrume.ru/mcp → initialize/tools/list/tools/call = 200, 4 инструмента, реальная карточка ГОСТ; тот же ключ на api.gostassistent.com/api/mcp → 401.
| № | Что | Где |
|---|---|---|
| 0 | Разделение сборок флагом AILAB_DISTRIBUTION. Скрытие служебных вкладок (биллинг, «О программе», экспорт/импорт/сброс, секции chat/workspace/safety/browser) выполняется только при AILAB_DISTRIBUTION=1. Чистый Hermes владельца собирается без флага и показывает всё. Реализовано: src/lib/build-flags.ts, vite.config.ts, settings/constants.ts, settings/index.tsx |
сборка |
| 1 | mcp_servers.gostassistent.url → https://gw.ailabscrume.ru/mcp, авторизация тем же ключом, что модели (через key_cmd), без отдельной переменной |
config.yaml |
| 2 | MCP_GOSTASSISTENT_API_KEY убрать из env_requires (сейчас required: false, но само наличие переменной создаёт путаницу) |
distribution.yaml |
| 3 | Имена инструментов станут gostassistent_<alias>-document_* (в агенте mcp__gostassistent_<alias>__*). Скиллы на старые имена не зашиты — проверено |
сборка |
| 4 | Ключ без object_permission.mcp_servers → 403 с понятным текстом, а не молчание |
шлюз/клиент |
| 5 | Тексты: INSTALL.md (сейчас «включается своим ключом в .env»), README.md стр. 97, CHANGELOG |
документы |
max_budget на них не действует).duda.v и anushov.k сейчас 0 MCP-серверов (проверено /key/list?return_full_object=true, 06.11.2026). После переезда они получат 403. Выдать право или зафиксировать, что инструменты им не положены?config.yaml целиком с -Force, без .bak и без мержа (проверено setup.ps1, строки 565–571). Правка url доедет до всех ночным автообновлением — но у тех, кто уже что-то настроил локально, настройки исчезнут. Делаем config.local.yaml + бэкап в этом релизе или отдельно?.env одна переменная ключа; grep api.gostassistent.com по профилю → 0 совпадений.initialize / tools/list / tools/call ключом сотрудника → 200.config.yaml и skills/ без бэкапа и мержа — правка в config.yaml без этого механизма бессмысленна для тех, кто уже настроил локальные дополнения.duda.v, anushov.k — без права MCP.