diff --git a/family/how-to/truenas-infrastructure.md b/family/how-to/truenas-infrastructure.md index a8cf7217..6adc605c 100644 --- a/family/how-to/truenas-infrastructure.md +++ b/family/how-to/truenas-infrastructure.md @@ -13,7 +13,7 @@ > - **⚠️ «Камера на TrueNAS» — ИСПРАВЛЕНО (2026-09-14, вечер-10) + ✅ РЕШЕНО ОКОНЧАТЕЛЬНО (вечер-13):** прежняя запись «камера = USB-вебка на t610, upstream `cam.*:8090` к камере отношения не имеет» — **❌ НЕВЕРНА**. Поиск в `Caddyfile.bak` доказал: **`cam.mallexxx.duckdns.org → 192.168.2.197:8090` — ЭТО И БЫЛА камера** на TrueNAS: отдельный HTTP-MJPEG-сервис (`ustreamer`/`mjpg-streamer`, порт 8090 — канон для «USB-вебка → MJPEG»). **Контейнер УТРАЧЕН** при пересоздании пула (локальный образ не пережил `.ix-apps`; из живого Caddyfile строка удалена — `cam.*` больше нет). **✅ ФИНАЛ (вечер-13):** вебка `046d:0825` физически в **t610**, работает через **аддон `a889bffc_go2rtc-hardware`** → RTSP H.264 `rtsp://192.168.2.176:8554/usb_camera_h264` → **Generic Camera** `camera.192_168_2_176` (зона `kotelnaia`, `unique_id`, **WebRTC работает**, поворот `#rotate=90`). Схема `camera: platform: ffmpeg` в Core — **❌ ОТВЕРГНУТА** (`Resource busy` + нет `unique_id`). Подробно — [[family/how-to/home-automation]] §5-кватер-И-3/И-6. > - **Не перенесено с TrueNAS:** ~~погашение TrueNAS-стека (Этап 4 п.6 — заблокировано: Caddy на TrueNAS держит точку входа)~~ → **✅ ЗАКРЫТО 2026-09-14 (ночь): стек автоматизации ПОГАШЕН, Caddy остался на TrueNAS (так и задумано).** См. §5-кватер-Л в [[family/how-to/home-automation]]. -> Обновлено: 2026-09-02 (vpn.mallexxx:443 = РАБОЧИЙ Xray-сервер, проверено end-to-end; vless-proxy outbound мёртв) +> Обновлено: 2026-09-15 (subscription-канал 3x-ui: ссылки `/sub/` работают, base64 vless-список; раздел `xray-admin` → «SUBSCRIPTION-КАНАЛ»). Ранее: 2026-09-02 (vpn.mallexxx:443 = РАБОЧИЙ Xray-сервер, проверено end-to-end; vless-proxy outbound мёртв) > ## ⚠️ ПРИОРИТЕТ 2026-09-01: VPS qentra.top УДАЛЁН → vless-proxy и сетевые туннели сломаны > - **VPS `91.207.28.205` больше НЕ существует** (вся его Xray/OpenVPN/WG-инфраструктура утрачена). Подробно: [[family/how-to/vps-qentra]]. @@ -245,6 +245,47 @@ arr, caddy, cups, filebrowser, gitea, hermes, immich, inpx-web, inpxer, library, ### `xray-admin` — 3x-ui панель (Xray server, добавлена 2026-09-01) +> ### ✅ SUBSCRIPTION-КАНАЛ (auto-обновление клиентов) — работает, проверено 2026-09-15 +> +> **Прямой ответ на вопрос Alex «можно поменять на xray subscription channel (auto)?»: да — он УЖЕ есть, включать ничего не надо. Ошибка была в том, что в клиент вбивали одиночный `vless://…`, а нужна ССЫЛКА ПОДПИСКИ.** +> +> | Что | Значение (проверено живьём 2026-09-15) | +> |---|---| +> | Базовый sub-URL | `https://vpn-panel.mallexxx.duckdns.org/sub/` → **HTTP 200** | +> | `user1` (direct, TrueNAS `90.189.160.148`) | `https://vpn-panel.mallexxx.duckdns.org/sub/68c5cy5n5ui138yh` | +> | `kraken-user` (egress через Kraken `92.62.70.41`) | `https://vpn-panel.mallexxx.duckdns.org/sub/f43074a029dc656e` | +> | Формат ответа | **base64-список `vless://`** (стандарт «subscription channel») | +> | Settings в `x-ui.db` | `subDomain=vpn-panel.mallexxx.duckdns.org`, `subPort=443`, `subScheme=https`; у каждого клиента своё поле **`subId`** (16 символов) и **`subId` есть только у клиентов `user1` / `kraken-user`** | +> +> **Разложенный payload `user1`** (для проверки совпадения с ручным конфигом): +> ``` +> vless://ce320965-6956-4759-84bb-7cb71cfc6252@vpn.mallexxx.duckdns.org:443?encryption=none&host=vpn.mallexxx.duckdns.org&path=%2Fvless&security=tls&type=ws#vless-ws-user1 +> ``` +> ``` +> vless://93a5dc4b-1b8d-4af5-9363-eb0091734293@vpn.mallexxx.duckdns.org:443?encryption=none&host=vpn.mallexxx.duckdns.org&path=%2Fvless&security=tls&type=ws#vless-ws-kraken-user +> ``` +> +> **Как включить авто-обновление в клиенте (3 шага):** ① удалить вручную вбитый профиль/сервер; ② «Add subscription» → вставить ПОЛНЫЙ путь `…/sub/` (без `/sub` не работает); ③ включить **auto update interval** (6–12 ч). Новые клиенты, добавленные в панели, после этого появляются в клиенте сами — руками `vless://` больше вбивать не нужно. +> +> **Команды проверки подписки (с TrueNAS, без правки чего-либо):** +> ```bash +> ssh truenas_admin@mallexxx.duckdns.org +> curl -sk -o /tmp/s.txt -w 'HTTP %{http_code} size %{size_download}\n' https://vpn-panel.mallexxx.duckdns.org/sub/ +> curl -sk https://vpn-panel.mallexxx.duckdns.org/sub/ | base64 -d # → vless://… построчно +> ``` +> **Где брать `subId`** (в контейнере нет `sqlite3`): +> ```bash +> docker exec xray-admin sh -c "strings /etc/x-ui/x-ui.db | grep -o '\"subId\":\"[a-z0-9]*\"'" +> ``` +> ⚠️ `jq`/`python3` в контейнере `xray-admin` не проверялись — использован `strings` (проверенный на TrueNAS приём, см. диагностические питфоллы ниже). +> +> **Что панель ТЕКУЩЕЙ версии (3.7.0) умеет и не умеет по подпискам:** +> - ✅ **Есть:** `subId` на клиента, sub-сервер (`/sub/` → base64 vless-список), настройки `subEnable/subPath/subURI/subJsonEnable/subJsonPath/subJsonURI/subClashEnable/subClashPath/subClashURI/subTitle/subListen/subUpdates/subDomain/subPort/subScheme/subCertFile/subKeyFile`, шаблоны `custom-subscription-templates`, таблица `sub_balancers`, `last_sub_fetch` в `client_traffics` (панель видит, кто и когда тянул подписку). +> - ❌ **НЕТ «subscription channel» как отдельной сущности с ветками/каналами** (типа mihomo-«channel»/Nekoray-«channel»): в бинарнике `/app/x-ui` **нет ни одной строки `channel` в этом смысле** и нет API-роутов каналов. Есть категории **`subJsonURI` / `subJsonPath`** — это JSON/Mihomo-формат той же подписки, `XUI`/`subClashEnableRouting` — mihomo-специфика. То есть «channel» = **ссылка подписки + клиентский auto-update**, отдельного механизма в панели нет. +> - Роуты панели по темам подписок (из `strings /app/x-ui`): `/panel/api/clients/sub`, `/panel/api/sub-balancers*`, `/panel/api/xray/outbound-subs*`. (`outbound-subs` = панель сама тянет ВНЕШНИЕ подписки в свои outbound — противоположное направление, не клиентская подписка.) +> +> **🔴 ОТКРЫТЫЙ ВОПРОС БЕЗОПАСНОСТИ (задан Alex 2026-09-15, решения НЕТ):** путь `/sub/` отдаётся **без авторизации** — `/sub/abc` тоже даёт 200. Кто знает `subId` — получает рабочий ключ. Возможный фикс: подписка по токену/`subUpdates`-проверке в настройках панели либо короткий `subId` + ротация. **Ничего не менялось, ждём решения.** + **Цель:** 3x-ui frontend для Xray-reverse туннеля Kraken↔TrueNAS (выход клиентов сети TrueNAS через Kraken). Подробно: [[personal/tech/xray-reverse-tunnel-kraken-truenas]]. | Параметр | Значение | diff --git a/personal/tech/deepseek-language-drift.md b/personal/tech/deepseek-language-drift.md new file mode 100644 index 00000000..135278e2 --- /dev/null +++ b/personal/tech/deepseek-language-drift.md @@ -0,0 +1,92 @@ +# DeepSeek — спонтанный переход на китайский + +**Дата:** 2026-09-15 +**Статус:** известный баг модели, официального фикса нет. Решение не принято. +**Проявление:** Кит (Whale) начал отвечать Alex'у на китайском вместо русского. + +## Симптом + +Модель `deepseek-chat` (провайдер `deepseek`, наш основной) выдаёт ответ +целиком на китайском, хотя весь диалог — на русском или английском. +Диалог был: коммиты git, английский технический вывод, короткие ответы. + +**Опять же**, баг плавающий: тот же промпт то даёт правильный язык, то нет. + +## Первопричина (подтверждена) + +Модель **рассуждает внутри на китайском** (это её дефолт — так эффективнее +по токенам), а финальный шаг «перевести на язык пользователя» иногда +пропускается или сбоит. + +Два фактора: + +1. **Мультиязычное reasoning по дизайну.** Внутренняя цепочка рассуждений + генерируется на китайском намеренно, для эффективности. Перевод обратно + не всегда происходит. +2. **Недетерминированность генерации.** Тот же вход иногда даёт английский, + иногда китайский. Поэтому ни один фикс не гарантирован. + +**Промпт игнорируется:** в багрепортах отмечено, что модель продолжает +на китайском **даже после прямой просьбы** не переключать язык. + +## Триггеры + +- **Короткие технические ответы без явной языковой привязки** — самый частый + триггер. Формулировка из разбора: «short, context-lacking prompts more + likely trigger default reasoning language». +- Большой объём англоязычного вывода (git-логи, diff'ы, термины) — + сбивает языковую привязку. +- В нашем system prompt **нет жёсткой языковой инструкции**: единственное + упоминание — строка 4 `SOUL.md`: `respond in the language you're + addressed in — Russian or English`. Формулировка мягкая. + +## Статус в апстриме + +Issue **открыты**, мейнтейнеры не ответили, официального фикса **нет**: + +- [DeepSeek-V3 #1226](https://github.com/deepseek-ai/DeepSeek-V3/issues/1226) — Model sometimes responds in Chinese even when the conversation is in English +- [DeepSeek-V3 #1443](https://github.com/deepseek-ai/DeepSeek-V3/issues/1443) — Model switches to Chinese despite all English prompts +- [DeepSeek-V3 #1463](https://github.com/deepseek-ai/DeepSeek-V3/issues/1463) — DeepSeek responds in Chinese instead of English + +Все решения ниже — community workarounds, не официальные. + +## Способы решения (по возрастанию стоимости) + +### 1. Жёсткая языковая инструкция в SOUL.md — дешёвый, не гарантирован + +Заменить мягкую формулировку на явную: + +> Отвечай **только** на языке собеседника — русский или английский. +> Никогда не используй китайский, даже внутри рассуждений. +> Если сомневаешься — пиши по-русски. + +**Плюс:** правится без кода, `SOUL.md` редактируется напрямую. +**Минус:** в багрепортах модель игнорировала и прямые запреты. + +Полезно ещё потому, что теперь есть `prompt_overrides` — можно вынести +формулировку в `review/*.md`, не трогая Python. См. [[hermes-agent-improvements]]. + +### 2. Детектор языка + retry — надёжно, дорого + +Программная проверка вывода: если китайский — повтор запроса. +Недетерминированность играет в нашу пользу: retry часто даёт правильный язык. + +**Требует кода в Hermes** (перехват ответа, детекция, повтор). Отдельная задача. + +### 3. Смена модели — радикально, эффективно + +Claude / GPT следуют языковым инструкциям заметно лучше. Если китайский +будет повторяться — вариант. + +**Открытый вопрос:** `deepseek-chat` выбран сознательно или это дефолт, +который никто не выбирал? Ответ определяет выбор между путями 1+2 и 3. + +## Что НЕ работает + +- Повтор той же просьбы «говори по-английски» — модель игнорирует. +- Опора на мягкую формулировку (`respond in the language you're addressed in`). + +## Связанное + +- [[hermes-whale-system-prompt]] — где живёт языковая инструкция (SOUL.md, строка 4) +- [[hermes-agent-improvements]] — prompt_overrides, способ правки без кода diff --git a/personal/tech/hermes-agent-improvements.md b/personal/tech/hermes-agent-improvements.md new file mode 100644 index 00000000..f241fa26 --- /dev/null +++ b/personal/tech/hermes-agent-improvements.md @@ -0,0 +1,131 @@ +# Hermes Agent — вынос хардкода промптов в файлы (prompt_overrides) + +**Дата:** 2026-09-15 +**Статус:** реализовано, закоммичено и запушено +**Репо:** `~/.hermes/hermes-agent` (сабмодуль, форк `mallexxx/hermes-agent`) + +## Проблема + +Стабильный слой system prompt был **захардкожен** в `agent/prompt_builder.py`. +Чтобы поменять формулировку любого блока (например, ужесточить инструкции +про язык ответа или про завершение задачи) — надо было править Python-код +сабмодуля. Это значит: правка живёт в форке, конфликтует при подтяжке +апстрима, и её надо переносить при каждом обновлении. + +## Решение: prompt_overrides + +Каждый хардкод-блок stable-слоя читается из `.md` файла. Маппинг — в конфиге. +Файла нет → fallback на хардкод, ничего не ломается. + +### Реализация (3 файла) + +**1. `agent/system_prompt.py`** — новая функция: + +```python +def _load_prompt_block(agent, block_name: str, default: str) -> str: + """Load a prompt block from a file if configured, else return default.""" + overrides = getattr(agent, "_prompt_overrides", None) or {} + path = overrides.get(block_name) + if path: + try: + resolved = os.path.expanduser(path) + content = Path(resolved).read_text(encoding="utf-8").strip() + if content: + return content + except Exception: + logger.debug("Could not load prompt override '%s' from %s", block_name, path) + return default +``` + +7 констант заменены на её вызовы: + +| Блок | Константа (fallback) | +|------|---------------------| +| `hermes_help` | `HERMES_AGENT_HELP_GUIDANCE` | +| `task_completion` | `TASK_COMPLETION_GUIDANCE` | +| `memory_guidance` | `MEMORY_GUIDANCE` | +| `session_search_guidance` | `SESSION_SEARCH_GUIDANCE` | +| `skills_guidance` | `SKILLS_GUIDANCE` | +| `tool_use_enforcement` | `TOOL_USE_ENFORCEMENT_GUIDANCE` | +| `execution_discipline` | `OPENAI_MODEL_EXECUTION_GUIDANCE` | + +**2. `agent/agent_init.py`** — чтение конфига в `agent._prompt_overrides` +(+11 строк, в секции рядом с tool-loop guardrail config). + +**3. `~/.hermes/hermes-whale/config.yaml`** — секция маппинга: + +```yaml +agent: + prompt_overrides: + hermes_help: ~/.hermes/hermes-whale/review/hermes_help.md + task_completion: ~/.hermes/hermes-whale/review/task_completion.md + memory_guidance: ~/.hermes/hermes-whale/review/memory_guidance.md + session_search_guidance: ~/.hermes/hermes-whale/review/session_search_guidance.md + skills_guidance: ~/.hermes/hermes-whale/review/skills_guidance.md + tool_use_enforcement: ~/.hermes/hermes-whale/review/tool_use_enforcement.md + execution_discipline: ~/.hermes/hermes-whale/review/execution_discipline.md +``` + +**4. `~/.hermes/hermes-whale/review/`** — 7 новых `.md` файлов с содержимым +блоков. Раньше в этой папке лежали только `memory_prompt.md` и `skill_prompt.md`. + +## Pitfall: prompt_overrides — это про Python-код, а не про config + +Файлы `review/*.md` **обязаны быть закоммичены**. Если закоммитить `config.yaml` +с путями, но не сами файлы — подмены промпта молча исчезнут (fallback на +хардкод), потому что `_load_prompt_block` ловит отсутствие файла в `except` +и отдаёт default. Ошибки нет, поведение просто возвращается к хардкоду. + +Это ровно то, что чуть не произошло: 7 файлов `review/*.md` были untracked. + +## Pitfall: порядок коммита сабмодуля + +Правки Python живут в сабмодуле. Порядок: + +```bash +cd ~/.hermes/hermes-agent && git add agent/*.py && git commit -m "..." +cd ~/.hermes && git add hermes-agent && git commit -m "..." # обновить пин +``` + +Если пин не обновить — в родительском репо видно +`hermes-agent (new commits, modified content)`, и `-dirty` в diff сабмодуля. + +## Коммиты и push (2026-09-15) + +| Репо | Коммит | Что | +|------|--------|-----| +| `hermes-agent` | `207a1de16` | feat: load stable system-prompt blocks from config prompt_overrides | +| `~/.hermes` | `7fd200c` | feat: wire prompt_overrides + whale-thread-guard multi-bot routing | +| `~/.hermes` | `7893c35` | chore: sync skills — curator consolidation | +| `~/.hermes` | `bdf84da` | chore(lsp): add vue/dockerfile/pyright/yaml language servers | +| `~/.hermes` | `176ce9c` | chore: thread-scoped memory store + claude token refresh script | +| `~/.hermes` | `9b111fc` | chore: untrack lsp/node_modules (1957 files, 43M) | + +**Запушено:** +- `hermes-agent` → `github.com:mallexxx/hermes-agent` (20 коммитов) +- `~/.hermes` → `git.mallexxx.duckdns.org/git_admin/eagle-hermes` (9 коммитов) + +## Осталось в коде (не вынесено) + +- `DEFAULT_AGENT_IDENTITY` — fallback, остаётся хардкодом +- `GOOGLE_MODEL_OPERATIONAL_GUIDANCE` — неактуально для Whale +- `COMPUTER_USE_GUIDANCE`, `KANBAN_GUIDANCE` — неактуально +- `PLATFORM_HINTS` — webhook там нет +- `build_skills_system_prompt()` — автогенерируется, файлом не заменить + +## Зачем это было нужно + +Два практических повода: + +1. **Правки промпта без правки Python** — теперь формулировки живут в `.md`, + редактируются в vault-стиле, не конфликтуют при подтяжке апстрима. +2. **Фикс языкового дрейфа DeepSeek** — нужна жёсткая языковая инструкция + в промпте; теперь её можно добавить файлом, а не патчем кода. + См. [[deepseek-language-drift]]. + +## Связанное + +- [[hermes-whale-system-prompt]] — полный состав system prompt (3 слоя) +- [[hermes-git-repo]] — структура репо, .gitignore, что коммитить +- [[deepseek-language-drift]] — баг с китайским выводом, повод для правок промпта +- [[hermes-fork-vs-upstream]] — расхождение с апстримом (24k коммитов) diff --git a/personal/tech/hermes-fork-vs-upstream.md b/personal/tech/hermes-fork-vs-upstream.md new file mode 100644 index 00000000..9dc32917 --- /dev/null +++ b/personal/tech/hermes-fork-vs-upstream.md @@ -0,0 +1,213 @@ +# Hermes — апстрим vs наш форк + +**Дата:** 2026-09-15 +**Статус:** актуально — анализ расхождения сделан, **стратегия подтяжки НЕ выбрана** +**Репо:** `~/.hermes/hermes-agent` (сабмодуль, форк `mallexxx/hermes-agent`) + +## Текущее решение: не принято + +На 2026-09-15 Alex'у представлены 3 варианта (A merge / B перенос на свежий +апстрим / C заморозка). **Выбор не сделан.** Работа над подтяжкой не начата. +Наш форк запушен и стабилен — можно спокойно решать. + +Открытый вопрос, определяющий выбор: **нужны ли нам вообще 24k коммитов +апстрима?** Мы используем Hermes как Zulip-бота с webhook-флоу; апстрим растёт +десктопом/TUI/платформами, которые нам не нужны. Точечный cherry-pick +(security-фиксы, фиксы `agent/`, новые модели) может быть достаточен. + +## Масштаб расхождения + +| Метрика | Значение | +|---------|----------| +| Коммитов апстрима впереди | **24 407** | +| Наших коммитов | 32 | +| Common ancestor | `4ed63170e4` (2026-06-04) | +| Дата последнего коммита апстрима | 2026-09-15 | +| Наша версия | `0.15.1` | +| Версия апстрима | `0.21.3` | +| Файлов изменено апстримом | 12 634 | +| Строк: +2 362 152 / −763 849 | | + +**Вывод:** это не «догнать на пару недель», а **~3.5 месяца и 24k коммитов**. +Апстрим ушёл на мажорные версии вперёд (0.15 → 0.21). + +## Ремоуты + +| Remote | URL | Роль | +|--------|-----|------| +| `origin` | `git@github.com:mallexxx/hermes-agent.git` | наш форк | +| `upstream` | `https://github.com/NousResearch/hermes-agent.git` | источник | + +## Главные изменения апстрима + +### 1. Платформы переехали из ядра в плагины ⚠️ КРИТИЧНО + +Апстрим **удалил** `telegram.py`, `slack.py`, `matrix.py`, `discord.py` из +`gateway/platforms/` — теперь они живут в `plugins/platforms/`. + +**Состав `plugins/platforms/` у апстрима (19):** +`a2a, buzz, dingtalk, discord, email, feishu, google_chat, homeassistant, irc, +line, matrix, mattermost, ntfy, photon, raft, simplex, slack, sms, teams, +telegram, wecom, whatsapp` + +**Наш `plugins/platforms/` (8):** `discord, google_chat, irc, line, mattermost, +ntfy, simplex, teams` + +**⚠️ Zulip НЕТ ни в ядре апстрима, ни в `plugins/platforms/` апстрима.** +Проверено: `git ls-tree -r upstream/main | grep -i zulip` → пусто. +Zulip — **полностью наша разработка**: +- `gateway/platforms/zulip.py` — не существует в апстриме +- `tools/zulip_thread_tool.py` — не существует в апстриме + +Это значит: наш Zulip-адаптер живёт в **устаревшем месте** (`gateway/platforms/`), +которое апстрим планомерно опустошает. При подтяжке его придётся переносить в +`plugins/platforms/` — иначе он повиснет на api, которого больше нет. + +### 2. Смежные подсистемы + +Топ-скоупы по числу feat/refactor коммитов: + +| Скоуп | Коммитов | Что это | +|-------|----------|---------| +| `desktop` | 773 | Десктоп-приложение — крупнейшая новая подсистема | +| `hermes_cli` | 607 | CLI переработан | +| `gateway` | 412 | Ядро гейтвея | +| `tools` | 339 | Инструменты | +| `agent` | 217 | Ядро агента | +| `cli` / `tui` / `tui_gateway` | 141/100/76 | TUI (Ink/React) | +| `state` | 106 | Персистентность сессий | +| `cron` | 91 | Планировщик | +| `computer_use` | 86 | Управление десктопом | +| `mcp` | 79 | MCP-клиент | +| `relay` | 54 | Новая подсистема релея | + +Типы коммитов: `fix` 10 994, `refactor` 4 158, `feat` 2 113, `test` 1 836. + +**Читается так:** апстрим прошёл большую волну рефакторинга (4158) + массовый +десктоп/TUI-фронт. Наши правки в `gateway/run.py` (13 коммитов) и +`gateway/stream_consumer.py` (9) — ровно в эпицентре этого рефакторинга. + +## Наши 32 коммита + +Сгруппированы по темам: + +**Zulip-платформа (наша, в апстриме отсутствует):** +- `b0f097a299` feat: add Zulip platform adapter (parallel to Discord) +- `ce093b0a75` zulip: switch approval prompt emojis ✅/❌ → 👍/👎 +- `b1e36eb2d7` fix(zulip): lock→locked emoji name +- `f39671ba97` fix(routing): pre_gateway_dispatch before session guard +- `d4e98a8b07` / `ef69e76413` / `40149225af` / `ddc6aa7c46` webhook: zulip in + BUILTIN_DELIVER_PLATFORMS, session_chat_id из X-Chat-Id +- `8255ea0c64` feat(zulip): session_search читает Zulip thread history +- `049922d8cc` fix(session_search): dual Zulip+SQLite recall, FTS5 sanitization + +**Webhook no-edit streaming (наш флоу без редактирования сообщений):** +- `fa271d2d19`, `ee41e8a847`, `9003d4cbbe` (revert), `1ac06d593a`, `9253fe52cc`, + `88cbb77b95`, `6c95153aa8`, `eb8dd7b5ed`, `a92840dcde`, `a08527e85c`, + `a88bec1762`, `5d3e95841b`, `ec9efce7e7` + +**Фичи профиля Whale:** +- `972004546f` feat: thread-scoped memory + configurable memory instruction +- `4ebad4f692` test: thread-scoped memory persistence, drift guard, snapshot +- `da146da5fa` feat: configurable background review prompts and allowed toolsets +- `207a1de16c` feat: load stable system-prompt blocks from config prompt_overrides + +**Прочее:** +- `6e09a3704a` fix(approval): interrupt pending approvals on user message +- `a17d02f31e` fix(api_server): tool.progress → plain-text chunks +- `81f10c306f` approval: reaction emoji на webhook + +## Риск конфликтов при merge + +Dry-run `git merge-tree` → **16 конфликтных файлов, 160 конфликтных маркеров**. + +| Файл | Наших коммитов | Коммитов апстрима | Риск | +|------|---------------|-------------------|------| +| `gateway/run.py` | 13 | **974** | 🔴 экстремальный | +| `cron/scheduler.py` | 1 | 307 | 🟠 | +| `agent/agent_init.py` | 3 | 226 | 🟠 | +| `gateway/platforms/base.py` | 2 | 208 | 🟠 | +| `gateway/platforms/api_server.py` | 1 | 177 | 🟠 | +| `agent/model_metadata.py` | 1 | 164 | 🟡 | +| `tools/approval.py` | 1 | 147 | 🟡 | +| `agent/prompt_builder.py` | 0 | 131 | auto-merge ok | +| `gateway/stream_consumer.py` | 9 | 73 | 🟠 (наш файл, апстрим переписал) | +| `agent/system_prompt.py` | 2 | 65 | 🟡 | +| `gateway/platforms/webhook.py` | 7 | 59 | 🟠 | +| `toolsets.py` | 2 | 62 | 🟡 | +| `tools/session_search_tool.py` | 2 | 41 | 🟢 | +| `tools/memory_tool.py` | 1 | 34 | 🟢 | +| остальные | 1 | <30 | 🟢 | + +**Худший случай:** `gateway/run.py` — 974 коммита апстрима против наших 13. +Это фактически переписывание файла. Конфликты там будут не «принять/отклонить +строку», а разбор чужой новой архитектуры. + +## Стратегии подтяжки + +### Вариант A: merge (сейчас) — НЕ рекомендуется +`git merge upstream/main` → 16 конфликтных файлов, ручной разбор 160 маркеров +в горячих файлах. Оценка: **дни на `run.py` + `stream_consumer.py`**, риск +потерять наши webhook/Zulip-правки. + +### Вариант B: перенос нашей функциональности на свежий апстрим (рекомендуется) +Взять чистый `upstream/main` как новую базу, затем **перенести наши фичи +патчами заново**: +1. Сохранить наши diff'ы как patch-файлы (`git format-patch`) +2. Форк от свежего апстрима +3. Переносить по темам: Zulip-адаптер (в `plugins/platforms/`!), 3 фичи Whale + (thread-scoped memory, bg-review, prompt_overrides), webhook-стриминг +4. Webhook-стриминг — вероятно **уже решён апстримом**, проверить перед переносом + +**Плюс:** результат на актуальной кодовой базе, без 24k коммитов долга. +**Минус:** ручная работа по переносу, ~1-2 дня. + +### Вариант C: заморозить и жить на форке +Принять, что мы отдельный продукт. Апстрим берём точечно (`git cherry-pick` +отдельных фиксов безопасности). Дёшево сейчас, растёт долг потом. + +## Ключевой вопрос перед подтяжкой + +**Нужны ли нам вообще 24k коммитов апстрима?** +Мы используем Hermes как **Zulip-бота с webhook-флоу**. Ядро апстрима +прирастает десктопом, TUI и платформами (telegram/slack/...), которые нам +не нужны. Реальная ценность от апстрима для нас: +- фиксы безопасности (`tools/tirith_security.py`, approval) +- фиксы багов в `agent/` (метаданные моделей, сжатие контекста) +- поддержка новых моделей/провайдеров + +Это можно брать точечно (cherry-pick), не таща весь долг. + +## Команды для проверки расхождения + +```bash +cd ~/.hermes/hermes-agent + +# Обновить инфу об апстриме +git fetch upstream + +# Насколько отстали +git rev-list --count main..upstream/main # апстрим впереди +git rev-list --count upstream/main..main # мы впереди + +# Общий предок +git merge-base main upstream/main + +# Изменения по нашим файлам +git diff --name-only $(git merge-base main upstream/main)..main | sort -u + +# Насколько апстрим трогал конкретный файл +git rev-list --count $(git merge-base main upstream/main)..upstream/main -- gateway/run.py + +# Dry-run merge (безопасно, не трогает рабочую копию) +git merge-tree --write-tree --name-only main upstream/main + +# Есть ли файл в апстриме +git cat-file -e upstream/main:gateway/platforms/zulip.py && echo yes || echo NO +``` + +## Связанное + +- [[hermes-git-repo]] — структура репо, .gitignore, что коммитить +- [[hermes-whale-system-prompt]] — prompt_overrides (наша фича) +- [[hermes-memory-architecture]] — thread-scoped память (наша фича) diff --git a/personal/tech/hermes-git-repo.md b/personal/tech/hermes-git-repo.md index 3137c241..291d68d9 100644 --- a/personal/tech/hermes-git-repo.md +++ b/personal/tech/hermes-git-repo.md @@ -89,10 +89,16 @@ cd ~/.hermes && git check-ignore -v .env.bak .env.new \ Каждый файл должен вывести строку `.gitignore::\t`. **Pitfall:** `.gitignore` НЕ влияет на уже отслеживаемые файлы. -`hermes-whale/lsp/node_modules/` остаётся в репо несмотря на правило `node_modules/`. +`hermes-whale/lsp/node_modules/` оставался в репо несмотря на правило +`node_modules/` (закрыто коммитом `9b111fc` через `git rm -r --cached`). Вывод — применять `git add` по именам файлов, а не `git add .`, иначе новое правило может дать ложное чувство защиты. +**Второй pitfall — `git add -f`:** после добавления правила `node_modules/` +попытка `git add` файла внутри него (например `lsp/node_modules/.package-lock.json`) +падает с «paths are ignored by one of your .gitignore files». Уже отслеживаемые +файлы коммитятся нормально без `-f` — правило на них не действует. + ## Что коммитить / что не коммитить **Коммитим:** @@ -111,10 +117,56 @@ cd ~/.hermes && git check-ignore -v .env.bak .env.new \ ## Долги (не исправлено, требуют решения Alex) -1. **`hermes-whale/lsp/node_modules/` в git** — 39M бинарников + `gopls` 41MB. - `.gitignore` не помогает (файлы уже отслеживаются). Чистка = `git rm -r --cached` + rewrite истории. -2. **`channel_directory.json` в .gitignore, но отслеживается** — правило добавлено после коммита. +1. **`channel_directory.json` в .gitignore, но отслеживается** — правило добавлено после коммита. В коммите `7fd200c` из него удалено 869 строк (файл в основном генерируемый). + Также живёт как ` M` в статусе постоянно — это рантайм-запись текущей сессии, + коммитить бессмысленно. Решение: `git rm --cached channel_directory.json`. +2. **История git не похудела.** 43M node_modules убраны из трекинга, но остаются + в истории. Для реальной экономии — `filter-repo` + force push (ломает клоны). + +### Закрыто 2026-09-15 + +- ~~`hermes-whale/lsp/node_modules/` в git~~ — **убрано из трекинга** коммитом + `9b111fc`: 1957 файлов / 43M. Файлы **остались на диске** (108 каталогов), + `git rm -r --cached` индекс не удаляет. Проверка: + `git ls-files | grep -c node_modules` → 0. + +## Коммиты и push (2026-09-15) + +| Коммит | Что | +|--------|-----| +| `207a1de16` (submodule) | load stable system-prompt blocks from config prompt_overrides | +| `7fd200c` | wire prompt_overrides + whale-thread-guard multi-bot routing | +| `7893c35` | sync skills — curator consolidation (516 удалений, 55 новых) | +| `bdf84da` | add vue/dockerfile/pyright/yaml language servers | +| `176ce9c` | thread-scoped memory store + claude token refresh script | +| `9b111fc` | untrack lsp/node_modules (1957 files, 43M) | + +**Запушено:** +- `hermes-agent` → `github.com:mallexxx/hermes-agent` — 20 коммитов +- `~/.hermes` → `git.mallexxx.duckdns.org/git_admin/eagle-hermes` — 9 коммитов + +Оба репо: `main...origin/main` синхронно, секретов в истории нет. + +## Утечка секретов (закрыта 2026-09-15) + +`.env.bak` и `.env.new` (в `~/.hermes/` и `~/.hermes/hermes-whale/`) содержали +**живые токены**: `TELEGRAM_BOT_TOKEN`, `OPENROUTER_API_KEY`, `DISCORD_BOT_TOKEN`, +`ZULIP_API_KEY`, `DEEPSEEK_API_KEY`. Игнорился только `.env` — варианты +`.bak`/`.new` НЕ покрывались. Один `git add .` затащил бы их в публичную историю. + +Также `webhook_subscriptions.json` (16-символьные per-endpoint секреты) — +не был игнорирован. Проверено фактически: `jq -r '.whale.secret | length'` → 16. + +Проверка, что секреты не попали в историю: +```bash +cd ~/.hermes && git log --all --diff-filter=A --name-only --pretty=format: | grep '\.env' +# пусто = чисто +``` + +`scripts/refresh-claude-token.sh` — **не секрет**: refresh token читается +из `~/.claude/.credentials.json` в рантайме, `CLIENT_ID` — публичный OAuth +идентификатор. ## Команды @@ -134,5 +186,6 @@ cd ~/.hermes && git log --all --diff-filter=A --name-only --pretty=format: | gre ## Связанное - [[hermes-whale-system-prompt]] — состав system prompt, prompt_overrides -- [[hermes-agent-improvements]] — вынос хардкода промптов в файлы +- [[hermes-agent-improvements]] — вынос хардкода промптов в файлы (механизм + коммиты) - [[hermes-memory-architecture]] — thread-scoped память +- [[hermes-fork-vs-upstream]] — расхождение с апстримом (24k коммитов) diff --git a/personal/tech/xray-reverse-tunnel-kraken-truenas.md b/personal/tech/xray-reverse-tunnel-kraken-truenas.md index b1cc9b5c..808c159d 100644 --- a/personal/tech/xray-reverse-tunnel-kraken-truenas.md +++ b/personal/tech/xray-reverse-tunnel-kraken-truenas.md @@ -380,6 +380,19 @@ TrueNAS: Rollback network fix: вернуть compose-бэкап на Kraken и выполнить `docker compose up -d`. Это вернёт нерабочий Docker bridge mode, поэтому делать только для диагностики/отката. +## ✅ Subscription-канал (auto-обновление клиентов) — 2026-09-15 + +> Проверено живьём: клиенты панели отдаются **ссылкой подписки**, а не только одиночным `vless://`. +> +> | Клиент | Ссылка подписки | Egress | +> |---|---|---| +> | `user1` | `https://vpn-panel.mallexxx.duckdns.org/sub/68c5cy5n5ui138yh` | `direct` → TrueNAS `90.189.160.148` | +> | `kraken-user` | `https://vpn-panel.mallexxx.duckdns.org/sub/f43074a029dc656e` | `via-kraken` → Kraken `92.62.70.41` | +> +> Ответ — **base64-список `vless://`** (стандартная подписка: `type=ws`, `security=tls`, `path=/vless`, `host=vpn.mallexxx.duckdns.org`). В клиенте: удалить ручной профиль → «Add subscription» с ПОЛНЫМ путём `…/sub/` → включить auto-update (6–12 ч). +> **Питфолл:** вбитый руками `vless://` авто-обновляться не будет — только ссылка подписки. Ссылка открыта без авторизации (кто знает `subId` — имеет ключ) — открытый вопрос к Alex. +> Полный разбор + команды проверки + что панель 3.7.0 умеет по подпискам: [[family/how-to/truenas-infrastructure]] → раздел `xray-admin` → «SUBSCRIPTION-КАНАЛ». + ## ✅ Per-user egress на `vpn.mallexxx.duckdns.org` (2026-09-02) В существующий VLESS-WS inbound `in-10095-tcp` добавлен второй клиент. Маршрутизация теперь различает клиентов по `email`: