hermes-vk-community 1.0.2__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (37) hide show
  1. hermes_vk_community-1.0.2/.gitignore +15 -0
  2. hermes_vk_community-1.0.2/PKG-INFO +233 -0
  3. hermes_vk_community-1.0.2/README.md +214 -0
  4. hermes_vk_community-1.0.2/docs/vk-rich-text-compatibility.md +77 -0
  5. hermes_vk_community-1.0.2/plugin.yaml +13 -0
  6. hermes_vk_community-1.0.2/pyproject.toml +33 -0
  7. hermes_vk_community-1.0.2/src/hermes_vk_community/__init__.py +3 -0
  8. hermes_vk_community-1.0.2/src/hermes_vk_community/adapter.py +1449 -0
  9. hermes_vk_community-1.0.2/src/hermes_vk_community/capabilities.py +35 -0
  10. hermes_vk_community-1.0.2/src/hermes_vk_community/cli.py +465 -0
  11. hermes_vk_community-1.0.2/src/hermes_vk_community/client.py +244 -0
  12. hermes_vk_community-1.0.2/src/hermes_vk_community/compat.py +137 -0
  13. hermes_vk_community-1.0.2/src/hermes_vk_community/config.py +173 -0
  14. hermes_vk_community-1.0.2/src/hermes_vk_community/data/vk-capabilities.json +36 -0
  15. hermes_vk_community-1.0.2/src/hermes_vk_community/errors.py +28 -0
  16. hermes_vk_community-1.0.2/src/hermes_vk_community/models.py +240 -0
  17. hermes_vk_community-1.0.2/src/hermes_vk_community/plugin.py +57 -0
  18. hermes_vk_community-1.0.2/src/hermes_vk_community/renderer.py +654 -0
  19. hermes_vk_community-1.0.2/src/hermes_vk_community/security.py +119 -0
  20. hermes_vk_community-1.0.2/src/hermes_vk_community/setup.py +248 -0
  21. hermes_vk_community-1.0.2/src/hermes_vk_community/storage.py +546 -0
  22. hermes_vk_community-1.0.2/src/hermes_vk_community/table_image.py +187 -0
  23. hermes_vk_community-1.0.2/tests/__init__.py +0 -0
  24. hermes_vk_community-1.0.2/tests/fixtures/vk/formatting/capability-profile.json +36 -0
  25. hermes_vk_community-1.0.2/tests/fixtures/vk/formatting/live-probe-2026-07-15.json +125 -0
  26. hermes_vk_community-1.0.2/tests/fixtures/vk/formatting/live-probe-format-data-2026-07-15.json +384 -0
  27. hermes_vk_community-1.0.2/tests/test_adapter.py +552 -0
  28. hermes_vk_community-1.0.2/tests/test_cli.py +26 -0
  29. hermes_vk_community-1.0.2/tests/test_client.py +127 -0
  30. hermes_vk_community-1.0.2/tests/test_config.py +76 -0
  31. hermes_vk_community-1.0.2/tests/test_plugin.py +76 -0
  32. hermes_vk_community-1.0.2/tests/test_renderer.py +171 -0
  33. hermes_vk_community-1.0.2/tests/test_security.py +84 -0
  34. hermes_vk_community-1.0.2/tests/test_setup.py +156 -0
  35. hermes_vk_community-1.0.2/tests/test_storage.py +197 -0
  36. hermes_vk_community-1.0.2/tests/test_table_image.py +22 -0
  37. hermes_vk_community-1.0.2/tests/test_tls_transport.py +157 -0
@@ -0,0 +1,15 @@
1
+ .DS_Store
2
+ .coverage
3
+ .pytest_cache/
4
+ .ruff_cache/
5
+ .venv/
6
+ .pyright/
7
+ __pycache__/
8
+ *.py[cod]
9
+ build/
10
+ dist/
11
+ htmlcov/
12
+ release-corpus/result.json
13
+ .env
14
+ .env.*
15
+ !.env.example
@@ -0,0 +1,233 @@
1
+ Metadata-Version: 2.4
2
+ Name: hermes-vk-community
3
+ Version: 1.0.2
4
+ Summary: VK Community Long Poll platform plugin for Hermes Agent
5
+ Author: Hermes VK Community contributors
6
+ License: MIT
7
+ Requires-Python: <3.14,>=3.11
8
+ Requires-Dist: aiohttp>=3.14.1
9
+ Requires-Dist: aiosqlite==0.22.1
10
+ Requires-Dist: filetype==1.2.0
11
+ Requires-Dist: hermes-agent>=0.18.2
12
+ Requires-Dist: markdown-it-py<5,>=4.0.0
13
+ Requires-Dist: packaging<27,>=24
14
+ Requires-Dist: pillow==12.2.0
15
+ Requires-Dist: pydantic-settings<3,>=2.7.0
16
+ Requires-Dist: pydantic<3,>=2.10.0
17
+ Requires-Dist: tenacity==9.1.4
18
+ Description-Content-Type: text/markdown
19
+
20
+ # Hermes VK Community
21
+
22
+ Плагин платформы [Hermes Agent](https://github.com/NousResearch/hermes-agent),
23
+ который подключает личного агента к сообщениям бота сообщества VK через
24
+ Community Long Poll API.
25
+
26
+ Проверяемый диапазон Hermes: `>=0.18.2,<0.19`.
27
+
28
+ Hermes `0.18.2` сам фиксирует `cryptography==46.0.7` и `Pillow==12.2.0`, для
29
+ которых опубликованы advisory с исправлениями в `48.0.1` и `12.3.0`.
30
+ До совместимого upstream-релиза supply-chain gate содержит только точечные
31
+ исключения конкретных advisory ID; расширять версии на стороне плагина нельзя,
32
+ поскольку это сделает dependency graph с Hermes `0.18.2` неразрешимым.
33
+
34
+ ## Быстрая настройка
35
+
36
+ После установки плагина включите его и запустите мастер:
37
+
38
+ ```bash
39
+ hermes plugins enable vk-community
40
+ hermes gateway setup
41
+ ```
42
+
43
+ В списке платформ выберите **VK Community**. Тот же мастер можно запустить
44
+ напрямую:
45
+
46
+ ```bash
47
+ hermes vk setup
48
+ ```
49
+
50
+ Мастер:
51
+
52
+ 1. расскажет, какие настройки включить в сообществе и какие права выдать ключу;
53
+ 2. примет ключ без отображения введённых символов;
54
+ 3. примет обычные ссылки на сообщество и профили пользователей;
55
+ 4. сам получит числовые VK ID;
56
+ 5. с подтверждением сделает сообщество приватным и включит сообщения;
57
+ 6. включит Community Long Poll `5.199` и событие входящего сообщения;
58
+ 7. проверит ключ, доступ к сообществу, пользователей и итоговые настройки;
59
+ 8. сохранит секрет и конфигурацию в активный профиль Hermes.
60
+
61
+ Если проверка не пройдёт, мастер не сохраняет локальную конфигурацию. Уже
62
+ подтверждённые изменения сообщества могут быть применены VK до последующей
63
+ ошибки проверки; повторный запуск безопасен, потому что настройки идемпотентны.
64
+
65
+ ## Создание сообщества и ключа
66
+
67
+ Подойдёт в том числе закрытое сообщество, в котором состоите только вы.
68
+
69
+ 1. [Создайте сообщество VK](https://vk.com/groups) или откройте его управление.
70
+ 2. Включите **Сообщения сообщества**.
71
+ 3. Настройки Long Poll можно оставить мастеру. При ручной настройке откройте
72
+ **Управление → Настройки → Работа с API → Long Poll API**, включите Long
73
+ Poll, выберите API `5.199` и отметьте только событие входящего сообщения
74
+ (`message_new`). Другие типы событий текущему адаптеру не нужны.
75
+ 4. Там же откройте **Ключи доступа** и создайте ключ сообщества.
76
+ 5. Выберите права:
77
+ - управление сообществом;
78
+ - сообщения сообщества;
79
+ - фотографии сообщества;
80
+ - документы сообщества.
81
+
82
+ Права на истории, стену, товары и заказы плагину не нужны. Права на фотографии
83
+ и документы используются реальными upload-flow: фото, обычные документы и
84
+ голосовые сообщения/TTS отправляются как нативные вложения VK. Для голоса
85
+ плагин преобразует исходное аудио в OGG/Opus через `ffmpeg`; если VK отклонит
86
+ voice-flow, безопасный fallback отправляет ZIP-документ без потери исходных байт.
87
+
88
+ Официальная отправная точка: [боты VK — начало работы](https://dev.vk.com/ru/api/bots/getting-started).
89
+
90
+ ## Где взять ID
91
+
92
+ Вручную извлекать числовые ID не обязательно: мастер принимает ссылки.
93
+
94
+ - `https://vk.com/club240186772` → ID сообщества `240186772`;
95
+ - `https://vk.com/id7750207` → ID пользователя `7750207`;
96
+ - `https://vk.com/shkarupa.alex` → мастер запросит числовой ID через VK API;
97
+ - короткая ссылка сообщества с собственным именем тоже разрешается через API.
98
+
99
+ Можно указать несколько разрешённых профилей через запятую. Пустой список
100
+ запрещён: доступ к личному агенту закрыт по умолчанию.
101
+
102
+ ## Где хранится ключ
103
+
104
+ Как и Telegram-токен в Hermes, ключ VK хранится под именем
105
+ `VK_COMMUNITY_TOKEN` в `.env` **активного профиля Hermes**, а не в YAML:
106
+
107
+ - профиль по умолчанию: обычно `~/.hermes/.env`;
108
+ - именованный профиль: его собственный каталог профиля и собственный `.env`.
109
+
110
+ Hermes устанавливает секретный scope конкретного профиля перед запуском
111
+ адаптера. Плагин читает ключ только через этот scope, поэтому при нескольких
112
+ профилях ключ одного профиля не подменит ключ другого.
113
+
114
+ Несекретные значения мастер записывает в `config.yaml` активного профиля:
115
+
116
+ ```yaml
117
+ platforms:
118
+ vk:
119
+ enabled: true
120
+ group_id: 240186772
121
+ allowed_user_ids:
122
+ - 7750207
123
+ typing_indicator: true
124
+ ```
125
+
126
+ Ключ, помещённый в `.env` этого репозитория, предназначен только для локальных
127
+ тестов разработчика. Он не переносится в профиль Hermes автоматически. При
128
+ обычной установке вводите ключ в мастере — тот использует штатную атомарную
129
+ запись секретов Hermes.
130
+
131
+ ## Разработка без установленного пользователем Hermes
132
+
133
+ Глобально или в пользовательскую установку Hermes ничего ставить не требуется.
134
+ `uv` создаёт изолированное окружение проекта и устанавливает совместимую версию
135
+ Hermes как зависимость плагина:
136
+
137
+ ```bash
138
+ uv sync --all-packages
139
+ uv run pytest packages/hermes-vk-community/tests
140
+ uv run ruff check packages/hermes-vk-community
141
+ uv run pyright packages/hermes-vk-community
142
+ ```
143
+
144
+ То есть тесты можно запускать до установки Hermes пользователем. Полностью без
145
+ кода Hermes проверить платформенный плагин нельзя: его контракт содержит классы
146
+ адаптеров, реестр платформ и профильное хранилище секретов. Изолированная `.venv`
147
+ решает это без изменения рабочей установки пользователя.
148
+
149
+ Для реальной end-to-end проверки всё равно нужны сеть, сообщество и тестовое
150
+ сообщение. Мастер уже выполняет безопасную часть live-проверки: API, права,
151
+ разрешение ID и получение Long Poll server.
152
+
153
+ ## Ручная настройка
154
+
155
+ Если мастер использовать нельзя, добавьте `VK_COMMUNITY_TOKEN` в `.env`
156
+ активного профиля и внесите показанный выше блок `platforms.vk` в его
157
+ `config.yaml`. Не помещайте токен в YAML: валидатор плагина намеренно отклоняет
158
+ такую конфигурацию.
159
+
160
+ После изменения перезапустите gateway и напишите со своего разрешённого профиля
161
+ в сообщения сообщества:
162
+
163
+ ```bash
164
+ hermes gateway restart
165
+ ```
166
+
167
+ Hermes передаёт адаптеру Markdown. Live-проверенный профиль API `5.199`
168
+ использует Unicode codepoint offsets и компилирует
169
+ `bold`/`italic`/`underline`/`url` в `format_data`; заголовки становятся жирными. Цитаты
170
+ передаются как `▎` плюс курсив, списки — как Unicode-маркеры с отступами.
171
+ Markdown-таблицы всегда рендерятся в JPEG и отправляются фотографиями. Поэтому
172
+ смешанный ответ может стать упорядоченной серией сообщений: текст, таблица,
173
+ следующий текст. Отдельная Markdown-картинка аналогично становится фото между
174
+ текстовыми сообщениями. HTML и исходный Markdown в VK отправлять нельзя — клиент
175
+ показывает их буквально.
176
+
177
+ ```bash
178
+ hermes vk probe-formatting --peer-id <PRIVATE_TEST_PEER_ID> \
179
+ --output vk-formatting-format-data-probe.json
180
+ ```
181
+
182
+ `auto` использует проверенный rich-профиль; явный `rich` доступен для API
183
+ `5.199`, а `plain` оставлен как детерминированный fallback. Unknown profile/API
184
+ по-прежнему переключает `auto` в plain и отклоняет явный rich. Probe проверяет
185
+ send/edit, Unicode offsets и readback, после чего best-effort удаляет тестовые
186
+ сообщения. Подробности и текущий проверенный профиль записаны в
187
+ [`docs/vk-rich-text-compatibility.md`](docs/vk-rich-text-compatibility.md).
188
+
189
+ Статус «печатает» отправляется через `messages.setActivity`, если
190
+ `typing_indicator: true`; live-тест подтвердил и ответ API, и отображение в
191
+ клиенте VK.
192
+
193
+ Release live-матрица дополнительно проверяет DM reply на реальное входящее
194
+ сообщение, pinned media download, `format_data`, inline keyboard, photo upload
195
+ и восстановление подготовленного outbox после закрытия/повторного открытия
196
+ SQLite. Для запуска нужны только `VK_COMMUNITY_TOKEN`, `VK_GROUP_ID` и
197
+ `VK_TEST_PEER_ID`; тестовый пользователь должен предварительно прислать фото.
198
+
199
+ ## Интерактивность, длинные ответы и pairing
200
+
201
+ Clarify-вопросы, подтверждения опасных команд и slash-confirm показываются как
202
+ одноразовые VK-клавиатуры. Payload — случайный opaque nonce, связанный в памяти
203
+ с разрешённым user ID, peer ID, сессией, допустимым действием и сроком жизни.
204
+ Повторный, просроченный или скопированный payload отклоняется до Hermes.
205
+
206
+ Длинные ответы и финализация streaming-preview поддерживают продолжения.
207
+ Доставленные части не отправляются повторно после timeout/частичной ошибки;
208
+ outbox сохраняет `random_id` и wire payload. После рестарта безопасные
209
+ `prepared`-строки досылаются, а неоднозначные `sending` переводятся в
210
+ `delivery_unknown` и доступны через `hermes vk doctor --delivery-unknown`.
211
+ Ошибка VK `914` уменьшает рабочий лимит вплоть до 256 символов и кеширует его.
212
+
213
+ Опциональный pairing включается в YAML:
214
+
215
+ ```yaml
216
+ platforms:
217
+ vk:
218
+ pairing:
219
+ enabled: true
220
+ code_ttl_seconds: 600
221
+ ```
222
+
223
+ Создайте одноразовый код командой `hermes vk pair` и отправьте его боту только
224
+ текстом. В SQLite хранится SHA-256, а не сам код; вложения, forwards и payload
225
+ от непривязанного пользователя не загружаются.
226
+
227
+ ## Диагностика
228
+
229
+ `hermes vk doctor` проверяет discovery/enabled, secret scope, конфликты политики,
230
+ identity сообщества, Community Long Poll, SQLite migration/schema, platform
231
+ lock, formatting profile и media flow. Флаги `--inflight` и
232
+ `--delivery-unknown` выводят только bounded идентификаторы и ошибки без текста
233
+ сообщений и секретов.
@@ -0,0 +1,214 @@
1
+ # Hermes VK Community
2
+
3
+ Плагин платформы [Hermes Agent](https://github.com/NousResearch/hermes-agent),
4
+ который подключает личного агента к сообщениям бота сообщества VK через
5
+ Community Long Poll API.
6
+
7
+ Проверяемый диапазон Hermes: `>=0.18.2,<0.19`.
8
+
9
+ Hermes `0.18.2` сам фиксирует `cryptography==46.0.7` и `Pillow==12.2.0`, для
10
+ которых опубликованы advisory с исправлениями в `48.0.1` и `12.3.0`.
11
+ До совместимого upstream-релиза supply-chain gate содержит только точечные
12
+ исключения конкретных advisory ID; расширять версии на стороне плагина нельзя,
13
+ поскольку это сделает dependency graph с Hermes `0.18.2` неразрешимым.
14
+
15
+ ## Быстрая настройка
16
+
17
+ После установки плагина включите его и запустите мастер:
18
+
19
+ ```bash
20
+ hermes plugins enable vk-community
21
+ hermes gateway setup
22
+ ```
23
+
24
+ В списке платформ выберите **VK Community**. Тот же мастер можно запустить
25
+ напрямую:
26
+
27
+ ```bash
28
+ hermes vk setup
29
+ ```
30
+
31
+ Мастер:
32
+
33
+ 1. расскажет, какие настройки включить в сообществе и какие права выдать ключу;
34
+ 2. примет ключ без отображения введённых символов;
35
+ 3. примет обычные ссылки на сообщество и профили пользователей;
36
+ 4. сам получит числовые VK ID;
37
+ 5. с подтверждением сделает сообщество приватным и включит сообщения;
38
+ 6. включит Community Long Poll `5.199` и событие входящего сообщения;
39
+ 7. проверит ключ, доступ к сообществу, пользователей и итоговые настройки;
40
+ 8. сохранит секрет и конфигурацию в активный профиль Hermes.
41
+
42
+ Если проверка не пройдёт, мастер не сохраняет локальную конфигурацию. Уже
43
+ подтверждённые изменения сообщества могут быть применены VK до последующей
44
+ ошибки проверки; повторный запуск безопасен, потому что настройки идемпотентны.
45
+
46
+ ## Создание сообщества и ключа
47
+
48
+ Подойдёт в том числе закрытое сообщество, в котором состоите только вы.
49
+
50
+ 1. [Создайте сообщество VK](https://vk.com/groups) или откройте его управление.
51
+ 2. Включите **Сообщения сообщества**.
52
+ 3. Настройки Long Poll можно оставить мастеру. При ручной настройке откройте
53
+ **Управление → Настройки → Работа с API → Long Poll API**, включите Long
54
+ Poll, выберите API `5.199` и отметьте только событие входящего сообщения
55
+ (`message_new`). Другие типы событий текущему адаптеру не нужны.
56
+ 4. Там же откройте **Ключи доступа** и создайте ключ сообщества.
57
+ 5. Выберите права:
58
+ - управление сообществом;
59
+ - сообщения сообщества;
60
+ - фотографии сообщества;
61
+ - документы сообщества.
62
+
63
+ Права на истории, стену, товары и заказы плагину не нужны. Права на фотографии
64
+ и документы используются реальными upload-flow: фото, обычные документы и
65
+ голосовые сообщения/TTS отправляются как нативные вложения VK. Для голоса
66
+ плагин преобразует исходное аудио в OGG/Opus через `ffmpeg`; если VK отклонит
67
+ voice-flow, безопасный fallback отправляет ZIP-документ без потери исходных байт.
68
+
69
+ Официальная отправная точка: [боты VK — начало работы](https://dev.vk.com/ru/api/bots/getting-started).
70
+
71
+ ## Где взять ID
72
+
73
+ Вручную извлекать числовые ID не обязательно: мастер принимает ссылки.
74
+
75
+ - `https://vk.com/club240186772` → ID сообщества `240186772`;
76
+ - `https://vk.com/id7750207` → ID пользователя `7750207`;
77
+ - `https://vk.com/shkarupa.alex` → мастер запросит числовой ID через VK API;
78
+ - короткая ссылка сообщества с собственным именем тоже разрешается через API.
79
+
80
+ Можно указать несколько разрешённых профилей через запятую. Пустой список
81
+ запрещён: доступ к личному агенту закрыт по умолчанию.
82
+
83
+ ## Где хранится ключ
84
+
85
+ Как и Telegram-токен в Hermes, ключ VK хранится под именем
86
+ `VK_COMMUNITY_TOKEN` в `.env` **активного профиля Hermes**, а не в YAML:
87
+
88
+ - профиль по умолчанию: обычно `~/.hermes/.env`;
89
+ - именованный профиль: его собственный каталог профиля и собственный `.env`.
90
+
91
+ Hermes устанавливает секретный scope конкретного профиля перед запуском
92
+ адаптера. Плагин читает ключ только через этот scope, поэтому при нескольких
93
+ профилях ключ одного профиля не подменит ключ другого.
94
+
95
+ Несекретные значения мастер записывает в `config.yaml` активного профиля:
96
+
97
+ ```yaml
98
+ platforms:
99
+ vk:
100
+ enabled: true
101
+ group_id: 240186772
102
+ allowed_user_ids:
103
+ - 7750207
104
+ typing_indicator: true
105
+ ```
106
+
107
+ Ключ, помещённый в `.env` этого репозитория, предназначен только для локальных
108
+ тестов разработчика. Он не переносится в профиль Hermes автоматически. При
109
+ обычной установке вводите ключ в мастере — тот использует штатную атомарную
110
+ запись секретов Hermes.
111
+
112
+ ## Разработка без установленного пользователем Hermes
113
+
114
+ Глобально или в пользовательскую установку Hermes ничего ставить не требуется.
115
+ `uv` создаёт изолированное окружение проекта и устанавливает совместимую версию
116
+ Hermes как зависимость плагина:
117
+
118
+ ```bash
119
+ uv sync --all-packages
120
+ uv run pytest packages/hermes-vk-community/tests
121
+ uv run ruff check packages/hermes-vk-community
122
+ uv run pyright packages/hermes-vk-community
123
+ ```
124
+
125
+ То есть тесты можно запускать до установки Hermes пользователем. Полностью без
126
+ кода Hermes проверить платформенный плагин нельзя: его контракт содержит классы
127
+ адаптеров, реестр платформ и профильное хранилище секретов. Изолированная `.venv`
128
+ решает это без изменения рабочей установки пользователя.
129
+
130
+ Для реальной end-to-end проверки всё равно нужны сеть, сообщество и тестовое
131
+ сообщение. Мастер уже выполняет безопасную часть live-проверки: API, права,
132
+ разрешение ID и получение Long Poll server.
133
+
134
+ ## Ручная настройка
135
+
136
+ Если мастер использовать нельзя, добавьте `VK_COMMUNITY_TOKEN` в `.env`
137
+ активного профиля и внесите показанный выше блок `platforms.vk` в его
138
+ `config.yaml`. Не помещайте токен в YAML: валидатор плагина намеренно отклоняет
139
+ такую конфигурацию.
140
+
141
+ После изменения перезапустите gateway и напишите со своего разрешённого профиля
142
+ в сообщения сообщества:
143
+
144
+ ```bash
145
+ hermes gateway restart
146
+ ```
147
+
148
+ Hermes передаёт адаптеру Markdown. Live-проверенный профиль API `5.199`
149
+ использует Unicode codepoint offsets и компилирует
150
+ `bold`/`italic`/`underline`/`url` в `format_data`; заголовки становятся жирными. Цитаты
151
+ передаются как `▎` плюс курсив, списки — как Unicode-маркеры с отступами.
152
+ Markdown-таблицы всегда рендерятся в JPEG и отправляются фотографиями. Поэтому
153
+ смешанный ответ может стать упорядоченной серией сообщений: текст, таблица,
154
+ следующий текст. Отдельная Markdown-картинка аналогично становится фото между
155
+ текстовыми сообщениями. HTML и исходный Markdown в VK отправлять нельзя — клиент
156
+ показывает их буквально.
157
+
158
+ ```bash
159
+ hermes vk probe-formatting --peer-id <PRIVATE_TEST_PEER_ID> \
160
+ --output vk-formatting-format-data-probe.json
161
+ ```
162
+
163
+ `auto` использует проверенный rich-профиль; явный `rich` доступен для API
164
+ `5.199`, а `plain` оставлен как детерминированный fallback. Unknown profile/API
165
+ по-прежнему переключает `auto` в plain и отклоняет явный rich. Probe проверяет
166
+ send/edit, Unicode offsets и readback, после чего best-effort удаляет тестовые
167
+ сообщения. Подробности и текущий проверенный профиль записаны в
168
+ [`docs/vk-rich-text-compatibility.md`](docs/vk-rich-text-compatibility.md).
169
+
170
+ Статус «печатает» отправляется через `messages.setActivity`, если
171
+ `typing_indicator: true`; live-тест подтвердил и ответ API, и отображение в
172
+ клиенте VK.
173
+
174
+ Release live-матрица дополнительно проверяет DM reply на реальное входящее
175
+ сообщение, pinned media download, `format_data`, inline keyboard, photo upload
176
+ и восстановление подготовленного outbox после закрытия/повторного открытия
177
+ SQLite. Для запуска нужны только `VK_COMMUNITY_TOKEN`, `VK_GROUP_ID` и
178
+ `VK_TEST_PEER_ID`; тестовый пользователь должен предварительно прислать фото.
179
+
180
+ ## Интерактивность, длинные ответы и pairing
181
+
182
+ Clarify-вопросы, подтверждения опасных команд и slash-confirm показываются как
183
+ одноразовые VK-клавиатуры. Payload — случайный opaque nonce, связанный в памяти
184
+ с разрешённым user ID, peer ID, сессией, допустимым действием и сроком жизни.
185
+ Повторный, просроченный или скопированный payload отклоняется до Hermes.
186
+
187
+ Длинные ответы и финализация streaming-preview поддерживают продолжения.
188
+ Доставленные части не отправляются повторно после timeout/частичной ошибки;
189
+ outbox сохраняет `random_id` и wire payload. После рестарта безопасные
190
+ `prepared`-строки досылаются, а неоднозначные `sending` переводятся в
191
+ `delivery_unknown` и доступны через `hermes vk doctor --delivery-unknown`.
192
+ Ошибка VK `914` уменьшает рабочий лимит вплоть до 256 символов и кеширует его.
193
+
194
+ Опциональный pairing включается в YAML:
195
+
196
+ ```yaml
197
+ platforms:
198
+ vk:
199
+ pairing:
200
+ enabled: true
201
+ code_ttl_seconds: 600
202
+ ```
203
+
204
+ Создайте одноразовый код командой `hermes vk pair` и отправьте его боту только
205
+ текстом. В SQLite хранится SHA-256, а не сам код; вложения, forwards и payload
206
+ от непривязанного пользователя не загружаются.
207
+
208
+ ## Диагностика
209
+
210
+ `hermes vk doctor` проверяет discovery/enabled, secret scope, конфликты политики,
211
+ identity сообщества, Community Long Poll, SQLite migration/schema, platform
212
+ lock, formatting profile и media flow. Флаги `--inflight` и
213
+ `--delivery-unknown` выводят только bounded идентификаторы и ошибки без текста
214
+ сообщений и секретов.
@@ -0,0 +1,77 @@
1
+ # VK rich-text compatibility
2
+
3
+ Live capability profile for the `hermes-vk-community` release gate.
4
+
5
+ ## Tested profile
6
+
7
+ - Date: 2026-07-15
8
+ - VK API: `5.199`
9
+ - Transport: community token, `messages.send` and `messages.edit`
10
+ - Community: private group
11
+ - Client observation: VK Web; the UI did not expose an exact build number
12
+ - Machine artifact:
13
+ `tests/fixtures/vk/formatting/live-probe-format-data-2026-07-15.json`
14
+ - Certified profile: `format-data-v1`, using Unicode codepoint offsets
15
+
16
+ ## Live results
17
+
18
+ | Candidate | API/readback | VK Web |
19
+ | --- | --- | --- |
20
+ | `format_data` bold, italic, underline, URL | accepted and returned | styled correctly |
21
+ | nested ranges | accepted and returned | styled correctly |
22
+ | partially overlapping ranges | accepted and returned | styled correctly |
23
+ | `messages.edit` plus `format_data` | accepted and returned | styled correctly |
24
+ | raw HTML | accepted as text | tags displayed literally |
25
+ | raw Markdown | accepted as text | markers displayed literally |
26
+ | 4096 Cyrillic characters | exact length/hash readback | accepted |
27
+ | bare HTTPS URL | exact readback | clickable |
28
+ | `messages.setActivity(type="typing")` | response `1` | visibly displayed |
29
+ | Unicode-codepoint emoji-adjacent ranges | accepted and returned | intended bold/underline ranges |
30
+ | UTF-16 emoji-adjacent ranges | accepted and returned | same intended ranges in VK Web |
31
+
32
+ The live Unicode probe established that the renderer's Unicode-codepoint
33
+ payload styles the intended emoji-adjacent ranges in VK Web. The competing
34
+ UTF-16 payload was also normalized/displayed as intended by the tested client;
35
+ this does not change the wire contract emitted by the plugin. Python string
36
+ indices are therefore certified for API `5.199`, while both probe cases remain
37
+ in the regression artifact for future client/API reverification.
38
+
39
+ ## Rendering profile
40
+
41
+ - Compile Markdown locally; never send raw HTML or Markdown markup.
42
+ - Map bold, italic and links to `format_data` version 1. Underline is supported
43
+ by the wire profile even though standard Markdown has no underline node.
44
+ - Render headings as bold text.
45
+ - Render quotes as `▎ ` followed by italic text.
46
+ - Render unordered and ordered lists with Unicode bullets/numbers and preserve
47
+ nesting with spaces.
48
+ - Render every Markdown table locally as one or more RGB JPEG images. JPEG is
49
+ the only table wire representation, independent of formatting mode.
50
+ - Lower a standalone Markdown image to a VK photo attachment. Keep an inline
51
+ image embedded in a text line as an alt-text fallback rather than guessing a
52
+ disruptive message split.
53
+ - Preserve mixed-content order by sending a sequence such as text → photo →
54
+ text. One logical Hermes reply is not required to fit one VK message.
55
+ - Rebase and clip formatting ranges after each 4096-character split.
56
+ - Persist each text chunk's exact `format_data` in the durable outbox so replay
57
+ does not silently lose styling.
58
+
59
+ `formatting.mode: auto` uses this rich profile. Explicit `rich` is accepted for
60
+ API `5.199`; `plain` remains a deterministic fallback that strips native text
61
+ ranges but retains JPEG tables. Unknown API/profile combinations continue to
62
+ fail closed to plain mode, and explicit `rich` is rejected for them.
63
+
64
+ ## Reverification
65
+
66
+ Run the private-peer probe after changing the VK API version or supported
67
+ clients:
68
+
69
+ ```bash
70
+ hermes vk probe-formatting --peer-id <PRIVATE_TEST_PEER_ID> \
71
+ --output vk-formatting-format-data-probe.json
72
+ ```
73
+
74
+ The command tests send/edit, nesting, partial overlap and competing Unicode
75
+ offset hypotheses, then removes its visible probe messages best-effort. Update
76
+ both the redacted artifact and the capability profile only after manual client
77
+ observation. Never commit a community token or private peer identifier.
@@ -0,0 +1,13 @@
1
+ name: vk-community
2
+ label: VK Community
3
+ kind: platform
4
+ version: 0.1.0
5
+ description: VK Community Long Poll gateway adapter for Hermes Agent.
6
+ author: Hermes VK Community contributors
7
+ requires_env:
8
+ - name: VK_COMMUNITY_TOKEN
9
+ description: VK community access token with messages, management, photos, and documents permissions
10
+ prompt: VK Community token
11
+ url: https://dev.vk.com/ru/api/bots/getting-started
12
+ password: true
13
+ category: credential
@@ -0,0 +1,33 @@
1
+ [build-system]
2
+ requires = ["hatchling>=1.27.0"]
3
+ build-backend = "hatchling.build"
4
+
5
+ [project]
6
+ name = "hermes-vk-community"
7
+ version = "1.0.2"
8
+ description = "VK Community Long Poll platform plugin for Hermes Agent"
9
+ readme = "README.md"
10
+ requires-python = ">=3.11,<3.14"
11
+ license = { text = "MIT" }
12
+ authors = [{ name = "Hermes VK Community contributors" }]
13
+ dependencies = [
14
+ "aiohttp>=3.14.1",
15
+ "aiosqlite==0.22.1",
16
+ "filetype==1.2.0",
17
+ "hermes-agent>=0.18.2",
18
+ "markdown-it-py>=4.0.0,<5",
19
+ "packaging>=24,<27",
20
+ "pillow==12.2.0",
21
+ "pydantic>=2.10.0,<3",
22
+ "pydantic-settings>=2.7.0,<3",
23
+ "tenacity==9.1.4",
24
+ ]
25
+
26
+ [project.entry-points."hermes_agent.plugins"]
27
+ "vk-community" = "hermes_vk_community"
28
+
29
+ [tool.hatch.build.targets.wheel]
30
+ packages = ["src/hermes_vk_community"]
31
+
32
+ [tool.hatch.build.targets.wheel.force-include]
33
+ "plugin.yaml" = "hermes_vk_community/plugin.yaml"
@@ -0,0 +1,3 @@
1
+ from hermes_vk_community.plugin import register
2
+
3
+ __all__ = ["register"]