chore(opencode): эксперимент с permission-reviewer — итоги и отключение

Плагин настроен и проверен (jev через Zen + прокси, enforce+deny),
но признан не стоящим использования: хрупкая цепочка, нервные
эскалации, запертая сессия. Запись в opencode.json отключена
(закомментирована), выводы — в docs/research/.
This commit is contained in:
ddadmin committed 2026-10-11 12:15:19 +03:00
1 parent 9f6db963be
commit 16e85139b9
4 files changed
+165 -11

No files matched your search

+8 -6
View File
@@ -1,11 +1,13 @@
{
"$schema": "https://opencode.ai/config.json",
"plugins": [
{
"package": "opencode-permission-reviewer@^2.4.0",
"options": {}
}
],
// Плагин opencode-permission-reviewer снят (эксперимент 2026-10-11 признан
// неудачным — см. docs/research/). Блок сохранён для возврата:
// "plugins": [
// {
// "package": "opencode-permission-reviewer@^2.4.0",
// "options": {}
// }
// ],
"default_agent": "build",
"permission": {
"write": "ask",
@@ -1,10 +1,17 @@
{
// Модель-ревьюер разрешений. Jev (System One) через OpenCode Zen:
// ключ OPENCODE_API_KEY читается из окружения процесса opencode (~/.env.secrets).
// Платный вариант без временного окна: opencode/jev-1.13
"model": "opencode/jev-1.13-free",
// Ниже порога уверенности решение уходит человеку
// Как устроена вся система автоподтверждения — см. permission-reviewer.md рядом.
// Модель-ревьюер разрешений: Jev (System One) через OpenCode Zen.
// Zen закрывает /zen/v1/systemone по региону (451) — нужен прокси
// (HTTPS_PROXY в ~/.env.secrets, сейчас hproxy.local:12334).
// Ключ OPENCODE_API_KEY читается из окружения процесса opencode.
"model": "opencode/jev-1.13",
// Ниже порога уверенности решение уходит дальше по цепочке
"confidenceThreshold": 0.7,
// enforce: политика рисков применяется (дефолт observe — только аудит)
"enforcementMode": "enforce",
// deny: финальная эскалация = отказ, а не запрос человеку
// (при permission=auto на запрос человека всё равно отвечает auto)
"escalationMode": "deny",
// Сложные/неуверенные решения Jev получают второе мнение чат-модели
// вместо эскалации к человеку. glm-5.3-flash из подписки opencode-go:
// лимиты в 4 раза выше, чем у deepseek-v4.1-flash, интеллект сравним.
@@ -0,0 +1,70 @@
# Система автоподтверждения разрешений (autoapprove)
Как устроено подтверждение команд в OpenCode на этой машине, и где что искать.
## Цепочка решений
Каждый `ask` из `opencode.json` проходит слои сверху вниз:
1. **Deny-паттерны** (`permission.bash` в `opencode.json`) — мгновенный отказ по
строке команды, до всяких моделей. Только секреты: `*.env*`, `*.secrets*`,
`~/.ssh/*` и т.п. Паттерны слепы к содержимому скриптов — это не рубеж, а
только защита секретов.
2. **Jev** (`opencode/jev-1.13`, System One) — типизированный ревьюер: получает
команду, свидетельства из транскрипта и содержимое локальных скриптов
(read-only enrichment), отвечает allow / deny / escalate.
3. **Второе мнение** (`opencode-go/glm-5.3-flash`) — разбирает трудные случаи,
где jev неуверен, но есть шанс на решение.
4. **Финал** — `escalationMode: deny`: неразрешённое отклоняется с причиной
агенту. Человек в цепочке не участвует: при `permission=auto` запрос к
человеку всё равно решил бы auto.
Ключевые настройки в `permission-reviewer.jsonc` рядом с этим файлом:
`enforcementMode: enforce` (применять политику рисков; дефолт observe — только
аудит), `escalationMode: deny` (дефолт manual — спросить человека).
## Инфраструктура
- Jev ходит на OpenCode Zen endpoint `/zen/v1/systemone`. Zen закрывает его
по региону (HTTP 451), поэтому сервису нужен прокси: `HTTPS_PROXY` в
`~/.env.secrets` (сейчас `hproxy.local:12334`). Чат-модели Zen
(`/zen/v1/chat/completions`) доступны без прокси — блокирован только
systemone.
- Ключ `OPENCODE_API_KEY` должен быть в окружении **процесса сервиса**:
`~/.env.secrets` сорсится из `~/.bash_custom`, сервис наследует окружение
шелла, из которого его запустили. После правки `.env.secrets`:
`source ~/.env.secrets && opencode service restart` — рестарт из старого
шелла ключ не подхватит.
- Jev нет в общем каталоге моделей OpenCode — это намеренно (не чат-модель),
на плагин не влияет: он зовёт systemone endpoint напрямую.
## Аудит и отладка
- Журнал решений: `~/.local/share/opencode/permission-reviewer-audit.jsonl` —
одна JSON-строка на ревью: вердикт, уверенность, причина, кто решал
(`decisionSource: system-one-reviewer / llm-reviewer / failure-safe`).
- Поле `reviewerEscalatedFrom` показывает, что первичный ревьюер (jev)
сработал, но эскалировал.
- `failure-safe` в `reason` = ревьюер недоступен (нет ключа, 451, таймаут) —
решение ушло в эскалацию.
- Emergency brake: команды, пахнущие выносом креденшелов наружу
(`curl -H "Authorization: ..."`), режутся детерминированно, мимо моделей.
## Известные свойства
- Сессия, полная опасных команд в транскрипте, выглядит для ревьюера
подозрительно: jev начинает эскаловать всё подряд, и с `deny` такая сессия
становится неработоспособной. Это fail-closed по замыслу. Лечение — работать
в чистой сессии.
- Тонкие пороги: `systemOneConfidenceThreshold` (дефолт 0.4) — насколько
уверенным должен быть jev, чтобы решать самому;
`systemOneReasoningThreshold` (дефолт 0.38) — сколько эскалаций уходит
на второе мнение.
## Документация
- Плагин и все опции: https://github.com/warc0s/opencode-permission-reviewer
(README на https://npm.io/package/opencode-permission-reviewer)
- Модель разрешений OpenCode V2: https://opencode.ai/v2/docs/permissions
- Jev у TypeSafe AI: https://jevmodel.org/ (есть прямой API с отдельным ключом
`TYPESAFE_API_KEY` — запасной маршрут, если Zen закроет systemone совсем)