1.7.0

Added (#372)

  • Per-server deferred-режим MCP (#372): если один MCP-сервер отдаёт тулов больше лимита, inline регистрируются только первые N, хвост скрывается из промпта и доступен через мета-тул tool_search — глобальный порог tool_search_threshold (=30) считается по всему каталогу и не спасал от одного «шумного» сервера на 50+ тулов при каталоге ниже порога. Лимит: глобальная настройка mcp_inline_tools_limit (дефолт 40, 0 — выключено) + per-server колонка mcp_servers.inline_tools_limit (миграция 118, SQLite+PG; 0 — наследовать глобальную, >0 — переопределить, -1 — без лимита; валидация -1..1000 в Create/Update/Import). Механика: registerMCPTools помечает тулы за лимитом registry.MarkToolDeferred (разметка — только поверх активного tool_search: порог > 0 и store реализует ToolSearcher, условие зеркально актору; ресурсный тул mcp_<server>_resources в лимите не участвует); ToolSearchMiddleware в BeforeAgent делит каталог по разметке — inline-часть остаётся видимой рядом с мета-тулом даже ниже глобального порога, deferred-хвост уходит в available/FTS-индекс; глобальный порог остаётся backstop (inline после сплита выше порога → весь каталог в поиск, прежний режим); BeforeModelRewriteState теперь сохраняет inline-дефы при forward selection; системный блок ## Tools фильтруется обёрткой toolSummaryFilter (Summaries+Has) — иначе хвост продолжал бы занимать промпт; выдача tool_search фильтруется по available, чтобы не обещать тулы, которые forward selection не добавит. Поля inline_tools_limit добавлены в API Create/Update (Update — pointer + копирование existing), JSON import/export конфига, WebUI (типы + поле в блоке TaigaClaw-расширений модалки импорта, валидация целого -1..1000). Удалён мёртвый internal/mcp/bm25_index.go (проект per-server deferred изначально задумывался с отдельным mcp_tool_search и in-memory BM25 — реализация переиспользует глобальный tool_search + FTS-индекс tool_index, ADR 2026-07-02 обновлён). Известные ограничения (осознанно, вне скоупа): resume-путь и child-run субагентов не подключают toolsearch-middleware вовсе (пре-существующий пробел #329) — там deferral не действует и каталог остаётся полным; follow-up issue заведён.

Review (#372)

  • ocr review — 18 находок, все разобраны (детали в комментарии ). Исправлены: [high] per-server override применялся при выключенном tool_search (разметка без потребителя) — весь лимитный путь загейчен на toolSearchActive; [high] FTS-индекс в per-server режиме строился из всего каталога, inline-тулы возвращались поиском, но молча отбрасывались при forward selection (ответ обещал то, чего не делал) — индексируются и выдаются только deferred (в global-backstop режиме deferred==каталог, поведение прежнее); [medium] UI-инпут принимал дробные значения (step="1" + Number.isInteger), явный 0 в патче импорта теперь уходит явно, а не молча опускается; [medium] MarkToolDeferred принимает только зарегистрированные имена, Register сбрасывает разметку при перезаписи имени (инвариант deferred ⊆ tools — защита от stale-пометок при reconnect hot-swap); no-op-ветка BeforeAgent явно сбрасывает состояние middleware; effectiveMCPInlineLimitresolvedMCPInlineLimit возвращает (limit, apply) без sentinel-семантики нуля; контракт toolSummaryFilter задокументирован (блок ## Tools строится один раз за ход, forward-selected тулы в него не ререндерятся; в global-backstop режиме блок может листить ToolDefs-скрытые тулы — тот же quirk глобального режима #329). Проверки: go test ./... -count=1, golangci-lint run ./... (0), go vet, make build-cross (6 платформ), npm run check, npm test — зелёные. Тесты: per-server активация ниже порога, forward selection с сохранением inline, поиск выбирает только deferred, глобальный backstop, неизвестные deferred-имена, toolSummaryFilter, resolvedMCPInlineLimit (таблица), инварианты registry (ghost-пометка no-op, re-register сбрасывает), round-trip колонки через миграцию 118 (SQLite) + guard pgMCPCols.