Repository navigation
Providers and Agents ru
English | 中文 | 日本語 | 한국어 | Español | Português | Русский
lib/llm-client.js — слой протоколов. Все четыре стрима делят один скелет openSseStream() (fetch / классификация ошибок / согласование бюджета / abort / SSE-обрамление / финализация); каждый протокол хранит только buildRequest + чистые парсеры событий. Извлечение SSE-полезной нагрузки data: единожды-источниковано (sseDataPayload, склейка multi-data-line по спеке):
| apiStyle | Эндпоинт | Примечания |
|---|---|---|
chat |
/v1/chat/completions |
также парсит DeepSeek-стиль reasoning_content
|
responses |
/v1/responses |
нативная мультимодальная запись input_*
|
anthropic |
/v1/messages |
явный cache_control на двух брейкпоинтах (system + последнее сообщение) — у Anthropic нет неявного префиксного кэширования |
runs |
Hermes /v1/runs
|
агентский протокол: одобрения / уточнения / инструменты / события мышления |
thinking: 'inline' | 'omit' (дефолт omit) управляет тем, едет ли текст размышлений ОДНИМ блоком <thinking> инлайн в дельта-стриме; только основной чат и detail thread используют inline. Поле reasoning.available в runs — реплей ответа, а не мышление — echo-guard обязан его выбрасывать (ADR-0004).
Форма запроса по провайдеру пересобирается четыре раза в основном чате (первичная / пересборка при переполнении / продолжение / перезапись таймкодов), унифицирована как createTurnRequest (prepare / rebuildFrom / continueWith / rewriteWith) — chat-handler держит КОГДА, лестница владеет КАК. Detail thread намеренно её не использует (семантика изолированных сессий, ADR-0007).
- Дефолт
max_tokens32768; серверы, которые ОТВЕРГАЮТ сверхлимитный бюджет с 400, получают один ретрайrenegotiateOutputCapс потолком, распарсенным из текста ошибки. -
finish_reason === 'length'→ ОДИН тихий проход продолжения (антиповторная инструкция, дельты глотаются); всё ещё обрезано → DONE несётoutputTruncated, панель тостит + показывает кнопку «продолжить» в один клик. Поля ручного max_tokens намеренно НЕТ — «ручка, требующая знания конкретной модели, — дизайнерский баг».
| Агент | Канал | Сессии |
|---|---|---|
| Hermes | /v1/runs |
серверные (sessionId в storage); части текущего хода используют канонические text/image_url, conversation_history — только строки (422 строгого слоя, проверено 2026-09-24) |
| OpenCode | HTTP opencode serve
|
серверные; случайный порт по умолчанию → рекомендуйте фиксированный --port
|
| OpenSquilla | локальный шлюз ws://…/ws
|
одна сессия шлюза на разговор; прикрепления >60K символов загружаются как документ page-context.md; allowlist origin — см. Модель безопасности |
| Agent Bridge | локальный демон @xiaohuzai/agent-bridge
|
адаптирует codex/claude/pi/gemini за одним HTTP-протоколом; подписочные логины работают как источники моделей |
Общий слой lib/agent-turn.js: агентский ход отправляет ТОЛЬКО текущий ход пользователя + хвостовой page-context прогон (транскрипт живёт на сервере; история никогда не пересылается). Изображения идут через pickTurnImages (≤8 изображений / бюджет URL ≤3MiB; гейты attach-времени и send-времени зеркалят друг друга). Переключение на агентского провайдера посреди разговора предлагает 「带上当前对话继续」 = одноразовый backfill (транскрипт простым текстом, потолок хвоста 200K символов).
Для агентских провайдеров транскрипт уже живёт на сервере (агентские ходы шлют только текущий ход) — «пусть UI самого агента подхватит» требует обнаруживаемости, а не перемещения данных. Две части:
-
Автоименование: после успешного хода Hermes одноразовый
PATCH /api/sessions/{id}(API исходного upstream; сервер санитизирует заголовки и отвергает точные конфликты) ставит заголовок вbrowsa:+ первая строка пользовательского текста текущего хода (потолок 48 символов). Семантика «штамп один раз» (hermesSessionTitled_<provider>, тот же жизненный цикл, что у id сессии): штампуются и успех, и 4xx (конфликт заголовков не должен превращаться в цикл ретраев на каждый ход); без штампа остаются только транспортные сбои (status 0) — до следующего успешного хода. У PATCH таймаут 10с, и его await соревнуется с потолком 2.5с, чтобы DONE никогда не зависал. Каналы на сегодня: Hermes (PATCH /api/sessions/{id}) и bridge/codex (POST /threads/{id}/titleдемона → app-serverthread/name/set, проверено вживую на codex 0.149.1 — имя сохраняется в state DB codex, иcodex resumeрезолвится по имени или id; вердикты видимости в Picker для четырёх bridge-агентов (проверено по исходникам 2026-10-01): codex ✓ (предикат пикераhas_user_event = 1 AND title <> ''— реальные ходы ему удовлетворяют; глобальная state DB, без cwd-скоупинга; именуется черезPOST /threads/{id}/titleдемона → app-serverthread/name/set). claude ✓ — посколькуtranscriptFix: 'claude'моста переписывает самоштампованныйentrypoint: sdk-*вcliна диске после каждого завершившегося хода (agent-bridge #56, подтверждено на Mac) — клиентского канала переименования нет (claude-agent-acp титулует сам), resume работает из пикера или по ID. pi ✓ (ACP-сессии попадают в собственное cwd-скоупнутое хранилище pi, которое читает пикер; переключатель скоупа + именованный фильтр; нативный RPC piset_session_nameпишет запись имениsession_info— pi-acp выставляет её только как команду/nameвнутри хода, к мосту пока не подключено). gemini ✓ (список исключает толькоkind:"subagent"; сессии моста —kind:"main"; хранилище~/.gemini/tmp/<cwd>/chats/, со скоупом по cwd; канала именования нет — заголовок берётся из первого сообщения пользователя). opencode/squilla не проверены. Выделенные сессии detail thread намеренно НЕ титулуются.) -
Отображение ID сессии:
storage.getAgentSessionInfoединолично владеет формами сессионных ключей по видам (bridge ключуется по эндпоинту черезactiveModel || baseUrl); панель сессий рисует строку «Сессия агента» (короткий id + копирование) над списком, скрытую для LLM-провайдеров / когда сессии нет.
Одобрения инструментов агента и запросы уточнений релеятся через lib/handlers/approval-relay.js (основной чат ключуется tabId, detail thread — subId). Форма pending-записи и ЕСТЬ интерфейс диспетчеризации. На стороне UI turn-chrome.js — оформление хода, общее для обеих поверхностей (индикатор ожидания, прогресс инструментов, карточки одобрений/уточнений, чип использования).
- Дропдаун перечисляет только СКОНФИГУРИРОВАННЫХ провайдеров, доступные первыми (стабильная сортировка); ноль сконфигурированных → один задизейбленный плейсхолдер.
- Если сохранённый activeProvider не сконфигурирован, первый сконфигурированный автовыбирается И сохраняется (починка состояния, не предпочтение); первый успешный ping тоже автопереключает (один раз).
- Мультимодельные провайдеры: ID моделей через запятую; по одному пункту дропдауна на модель;
resolveChatModelуважает activeModel, только пока тот принадлежит этому провайдеру. - Обе группы (LLM / агент) рендерятся карточками с табами (группа агентов свёрнута по умолчанию; порядок табов [bridge, opencode, hermes] закреплён тестом).
Системный промпт каждого CHAT-хода = пользовательский systemPrompt + строка языка ответа + CAPABILITY_HINTS + CHOICE_REQUEST_HINT; он должен оставаться байт-стабильным префиксом (поключевое гейтирование по ходам ломает KV-кэш промпта с позиции 0). Каждая оставшаяся запись окупилась реальным багом рендеринга — не «ужесточайте» why-пункт, не прочитав его историю в AGENTS.md. Лечение пропущенного формата — лучший хинт, а не рантайм-детекция.
Авторитетные версии: Providers-and-Agents (англ.) / Providers-and-Agents-zh (кит.) — снимок первичного перевода ИИ, синхронизирован 2026-10-01.
English
- Home
- Architecture
- Rendering Pipeline
- Storage Model
- Providers and Agents
- ASR and Video Analysis
- Security Model
- Design Decisions
- Contributing
中文
相关 / Related
日本語
한국어
Español
- Inicio
- Arquitectura
- Pipeline de renderizado
- Modelo de almacenamiento
- Proveedores y agentes
- ASR y análisis de vídeo
- Modelo de seguridad
- Decisiones de diseño
- Contribuir
Português
- Início
- Arquitetura
- Pipeline de renderização
- Modelo de armazenamento
- Provedores e agentes
- ASR e análise de vídeo
- Modelo de segurança
- Decisões de design
- Contribuindo
Русский