diff --git a/personal/projects/personal-os/claude-proxy-node-internals.md b/personal/projects/personal-os/claude-proxy-node-internals.md index ba1abdd8..f9248609 100644 --- a/personal/projects/personal-os/claude-proxy-node-internals.md +++ b/personal/projects/personal-os/claude-proxy-node-internals.md @@ -84,21 +84,41 @@ CMD ["claude-proxy", "3456"] В режиме `print` проблема та же — proxy ждёт завершения `claude --print` и возвращает результат целиком, не транслируя промежуточный вывод. -### 3. claude spawn'ит свой bash без контекста Hermes +### 3. Claude CLI сам исполняет инструменты вместо Hermes -**Механизм:** proxy запускает `claude` через `child_process.spawn('claude', args, {env: process.env})`. Ниже по цепочке: +**Проблема:** Claude Code CLI **сам выполняет Bash/Read/Write/Edit инструменты внутри своей сессии**, а не возвращает tool_use блоки Hermes'у для исполнения. + +Текущий flow: ``` -launchd → claude-proxy-start.sh → claude-proxy (node) → claude CLI (subprocess) - └── bash -c "..." +Hermes → claude-proxy (Node.js) → claude CLI + └── сам делает Bash, Read, Write + └── bash -c "..." ``` -Claude Code CLI для выполнения инструментов (Bash, Write, Edit) внутри себя спавнит `bash -c "..."`. Этот bash: -- наследует env от `claude` процесса -- НЕ имеет `HERMES_HOME`, кастомного `PATH`, `SHELL=/bin/zsh` с профилем Hermes -- использует системный `/bin/bash` или `/bin/sh` +Hermes видит только финальный текст ответа. Он теряет контроль над: +- какие файлы читаются +- какие команды выполняются +- кто их логирует и аудитит +- allower/denier политики Hermes игнорируются -В контейнере (alpine) bash вообще может отсутствовать — claude CLI использует `/bin/sh`. +Правильный flow (caller-dispatched tools): + +``` +Hermes → claude-proxy (Node.js) → claude CLI (только генерация) + ↓ текст ответа с tool_use блоками +Hermes ── парсит tool_use ── выполняет через свои инструменты ── результат → обратно в claude CLI +``` + +Для этого proxy должен: +1. Выключить инструменты в `claude` CLI — передать `disallowed_tools = ["Bash", "Read", "Write", "Edit", "Glob", "Grep"]` +2. Остановиться после первого assistant-сообщения +3. Отдать tool_use блоки Hermes'у как OpenAI `tool_calls` +4. Принять результат выполнения от Hermes и передать обратно в `claude` для продолжения (multi-turn) + +**Текущий `openclaw-claude-proxy` не поддерживает этот flow корректно:** stream-json протокол для multi-turn interaction сломан начиная с claude CLI 2.1.141. В режиме `print` multi-turn вообще невозможен. + +**Python wrapper (`claude-code-openai-wrapper`) этот flow поддерживает** — у него есть `stop_after_first_assistant` и `disallowed_tools`. Именно поэтому он активен, а Node.js — fallback. ## Предложенные подходы @@ -113,9 +133,3 @@ Claude Code CLI для выполнения инструментов (Bash, Writ 1. **Проброс stderr:** pipe'ить stderr `claude` в stderr proxy — видно в `docker logs` или launchd логах. 2. **Режим `print`:** переключить `CLAUDE_PROXY_RUNTIME=print` — теряется производительность stream-json, но логов больше. 3. **Node.js `stream-json` мониторинг:** логировать каждую N-ную JSON строку из stdout claude для отладки. - -### Против изоляции shell - -1. **В Docker:** установить bash в контейнер (`apk add bash`), установить `SHELL=/bin/bash` в env. -2. **Передача `HERMES_HOME`:** смонтировать `~/.hermes` в контейнер и передать `HERMES_HOME` в env claude CLI. -3. **wrapper-скрипт для claude:** заменить бинарник `claude` в контейнере на shell wrapper, который ставит профиль Hermes перед вызовом реального `claude`.