andrey f64ee94e93 opencode-fixes: drop leak length cap, strip fences instead of vetoing
The 1200-char cap silently disabled the tool-call leak retry: a leaked
shell call with a heredoc body is routinely longer than that (missed live
at 1604 chars), and the rejection path logged nothing so the miss was
invisible. Detection is structural already - the tags must anchor the
message, finish is "stop" and no tool parts are present - so length is not
evidence of legitimacy. The cap is now opt-in via OPENCODE_FIXES_LEAK_MAX_LEN
(0 = unlimited, the default).

Also replace the blanket includes("```") veto with stripping fenced blocks:
a legal "the format looks like this" example loses its tags and stays a
clean answer, while a real leak whose command body contains fences is still
detected.

Self-tests: regression fixture for the long heredoc leak, fenced-suffix
example, fence-inside-command leak and inline-code suffix; the cap case is
only registered when a cap is configured.
2026-11-07 00:05:26 +03:00

opencode-fixes

Плагин-«ремонт» для OpenCode v2 против типичных проблем self-hosted inference (llama.cpp / llama-swap / vLLM и т.п.):

Фикс Симптом Что делает
bool-args Invalid input: expected boolean, received "True" — модель отдаёт "True"/"FALSE" строками вместо булевых аргументов инструментов Приводит строки к true/false, но только в позициях схемы, доказуемо булевых (Effect-схемы нативных инструментов и JSON-схемы плагинов/MCP). Обычные строковые поля не трогает
toolcall-leak Сессия «зависает»: модель сгенерировала вызов инструмента, сервер не распарсил его и вернул как обычный текст с finish_reason=stop; OpenCode считает ход финальным ответом Распознаёт протёкший tool call и автоматически просит модель повторить действие настоящим вызовом инструмента

Установка

Вариант 1 — файл (самый простой): скопируйте index.ts в

~/.config/opencode/plugins/opencode-fixes.ts     # глобально
<проект>/.opencode/plugin/opencode-fixes.ts      # для одного проекта

OpenCode v2 подхватывает файлы из этих папок автоматически, без рестарта.

Вариант 2 — путь в конфиге: склонируйте репозиторий и пропишите в ~/.config/opencode/opencode.json:

{ "plugin": ["/абсолютный/путь/до/opencode-fixes/index.ts"] }

Зависимостей нет: только node:fs/os/path и рантайм-модуль @opencode/plugin, который предоставляет OpenCode.

Как работает toolcall-leak

Ход считается «протёкшим», если он:

  • завершился с finish=stop (не ошибка и не настоящий tool call);
  • не содержит ни одного tool-вызова;
  • его текст ≤ 1200 символов, без markdown-код-блоков;
  • начинается с <tool_call> или заканчивается на </tool_call>.

Действие: в сессию отправляется короткое системное уведомление с просьбой повторить действие настоящим вызовом инструмента.

Защита от петель: максимум 3 ретрая подряд, счётчик сбрасывается первым же успешным ходом. Кумулятивных лимитов нет — в длинной сессии каждая новая утечка чинится штатно. Есть дедупликация между инстансами плагина (хот-релоад может оставить старые копии) — ретрай всегда ровно один на утечку.

Настройка (переменные окружения)

Переменная По умолчанию Значение
OPENCODE_FIXES_LOG ~/.local/state/opencode/opencode-fixes.log путь к логу
OPENCODE_FIXES_LEAK_MAX_LEN 1200 максимальная длина «утёкшего» текста
OPENCODE_FIXES_LEAK_MAX_CONSECUTIVE 3 ретраев подряд до сдачи
OPENCODE_FIXES_DEBUG — подробные логи bool-фикса

Диагностика

При старте плагин прогоняет self-test обоих фиксаторов (4 + 8 кейсов). Полезные строки лога:

bool self-test True: PASS
bool: repaired shell: {"command":"ls","background":true}
leak self-test prefix-leak: PASS
leak: tool call leaked as text (pid ... ses_... msg_...), retry 1/3
leak: clean turn, consecutive reset (ses_...)

Требования

OpenCode v2 (API Plugin.define). Проверено с серверами на базе llama.cpp (llama-swap), vLLM и совместимыми OpenAI API.

Лицензия

MIT

S
Description
No description provided
Readme MIT
42 KiB
0 Stars 1 Watchers 0 Forks
Languages
TypeScript 100%