diff --git a/reports/pr_hermes_sandbox_env_propagation_1_closure.md b/reports/pr_hermes_sandbox_env_propagation_1_closure.md new file mode 100644 index 0000000..9dc4d89 --- /dev/null +++ b/reports/pr_hermes_sandbox_env_propagation_1_closure.md @@ -0,0 +1,148 @@ +# PR-Hermes-Sandbox-Env-Propagation-1 Closure Report + +**Date**: 2026-05-17 +**선행 PR**: PR-Hermes-Docsrv-Search-1, PR-Hermes-WebSearch-1, PR-Hermes-ToolCall-Adapter-1 +**범위**: 1-line config 변경 (`terminal.env_passthrough` allowlist) +**파일**: `~/.hermes/config.yaml` + +## Summary + +PR-Hermes-Docsrv-Search-1 / PR-Hermes-WebSearch-1 의 user-facing E2E 마지막 조각. PR-Hermes-ToolCall-Adapter-1 closure 시 Layer 2 Hermes chat 의 `execute_code` 샌드박스가 HERMES_DOCSRV_TOKEN env 를 inherit 못 함 → DS API 401 발견. Hermes 의 `terminal.env_passthrough` 메커니즘 (skill 의 `required_environment_variables` 또는 user-config allowlist 가 sandbox 의 env 스트립을 우회) 에 1줄 추가로 해결. + +## Root cause + +`tools/env_passthrough.py` docstring 정리: +- `execute_code` / `terminal` 샌드박스는 기본적으로 모든 env 변수를 strip (보안) +- 두 가지 path 로 allowlist 등록 가능: + 1. **Skill `skill_view` tool_call** 시 → `register_env_passthrough(required_environment_variables)` 자동 발화 + 2. **User config `terminal.env_passthrough`** → 영구 allowlist + +**기존 docsrv_ask SKILL.md 의 frontmatter**: +```yaml +prerequisites: + commands: [curl, jq] + env: [HERMES_DOCSRV_TOKEN] +``` + +→ legacy `env_vars` 형식. `skill_view` 가 호출되어야 변환 + register. 그러나 `hermes chat -s docsrv_ask` preload 는 **system prompt inject 만**, `skill_view` 호출 발화 안 됨 → allowlist 등록 0 → sandbox 가 HERMES_DOCSRV_TOKEN strip → 401. + +## Fix + +`~/.hermes/config.yaml` 의 terminal section 1줄 변경: + +```yaml +terminal: + ... + env_passthrough: + - HERMES_DOCSRV_TOKEN # PR-Hermes-Sandbox-Env-Propagation-1 + ... +``` + +**보안 검증** (GHSA-rhgp-j443-p4rf 정합): +- `_HERMES_PROVIDER_ENV_BLOCKLIST` 가 ANTHROPIC_*, OPENAI_*, CLAUDE_API_KEY 등 Hermes-managed provider 토큰 차단 +- HERMES_DOCSRV_TOKEN 은 user-managed 토큰 (voice-memo-bot account JWT) → blocklist 외 → 안전 + +## 검증 + +### 1. Direct config verification + +```bash +$ HERMES_DOCSRV_TOKEN=... ~/.hermes/hermes-agent/venv/bin/python -c ' +from tools.env_passthrough import is_env_passthrough, _load_config_passthrough +print(_load_config_passthrough()) +print(is_env_passthrough("HERMES_DOCSRV_TOKEN")) +print(is_env_passthrough("CLAUDE_API_KEY")) +' + +parent process has HERMES_DOCSRV_TOKEN: True (len=157) +config terminal.env_passthrough loaded: ['HERMES_DOCSRV_TOKEN'] +HERMES_DOCSRV_TOKEN in allowlist: True ✅ +CLAUDE_API_KEY in allowlist (should be False): False ✅ +``` + +### 2. Hermes chat E2E (이전 401 시나리오 재현) + +``` +hermes chat -s docsrv_ask -q 'docsrv_ask 으로 내 자료에서 voice memo 관련 자료 찾아줘' +``` + +**Proxy 로그 trace** (이전 vs 현재): + +| 단계 | 이전 (Adapter A only) | 현재 (Adapter A + env_passthrough) | +|---|---|---| +| Hermes Turn 1 | Gemma → execute_code(curl) | Gemma → execute_code(curl) | +| Sandbox env | HERMES_DOCSRV_TOKEN STRIPPED | HERMES_DOCSRV_TOKEN PROPAGATED ✅ | +| DS API call | HTTP 401 "유효하지 않은 토큰" | **HTTP 200** ✅ | +| DS RAG pipeline | (skip) | query_analyze + classifier + evidence + synthesis ALL RUN ✅ | +| Result | Hermes 401 loop (4 turn) | **Real corpus answer with 2 citations** | + +**DS log 발췌** (보고서 작성 시점, voice memo query): +``` +[INFO] query_analyze ok query='voice memo' conf=0.90 intent=semantic_search elapsed_ms=6692 +[INFO] evidence ok query='voice memo' candidates=2 kept=2 elapsed_ms=10665 +[INFO] classifier ok query='voice memo' verdict=sufficient (raw=partial) covered=1 missing=2 elapsed_ms=13076 +[INFO] synthesis ok query='voice memo' evidence_n=2 answer_len=186 citations=2 conf=medium elapsed_ms=3948 +[INFO] ask query='voice memo' results=10 evidence=2 cite=2 synth=completed conf=medium completeness=full refused=False total=17363 +``` + +**DS synthesis 결과** (실제 corpus, hallucinated 아님): +> "The evidence mentions voice memos as one of the things to add joy to your day [2]. Additionally, there is a test voice memo related to the first review item for gas engineer studies [1]." + +Citations: +- [1] "테스트 음성 메모입니다. 가스기사 학습 검토 항목 첫 번째" — 실제 DS memo (test-voice-memo) +- [2] "Voice memos, snail mail and your own private screening room." — "The Good List: 6 Things to Add Joy to Your Day" 문서 + +### 3. Layer 2 user-facing 응답 + +Hermes 가 DS 응답 받은 후 multi-turn agent loop 에서 추가 docsrv_ask 호출 (refinement) — 응답 합성에 ~5-10분 소요 (Mac mini 26B + 23000 input tokens). Background 중단했으나 모든 핵심 검증 (env propagation + DS 실 호출 + 실 citations) 통과. + +## File changes + +### Mac mini +- `~/.hermes/config.yaml`: + - `terminal.env_passthrough: [] → [HERMES_DOCSRV_TOKEN]` +- `~/.hermes/config.yaml.pre-env-passthrough.20260517` (7일 안전망) + +### 변경 없음 +- SKILL.md (정상 동작 — sandbox env가 propagate 되므로 skill 본문 변경 불필요) +- mlx-proxy.py +- DS code +- Hermes gateway / launchagent + +## 결정 사항 + +1. **PR-Hermes-Sandbox-Env-Propagation-1 = SHIPPED**: + - Direct config verification PASS + - Hermes chat E2E DS API 200 + real corpus citations 확보 + - **PR-1 / PR-2 user-facing E2E 마지막 조각 풀림** +2. **남은 운영 관심사** (별 트랙): + - **PR-Hermes-Skill-Curl-Refine-1** (선택): docsrv_ask SKILL.md frontmatter 를 legacy `prerequisites.env` → 표준 `required_environment_variables` 로 마이그레이션. 효과 = `skill_view` 도 자동 registration. 본 config-level fix 와 중복 안전망. P3. + - **PR-Hermes-Multi-Turn-Refinement-1** (선택): Gemma 가 첫 docsrv_ask 결과로 만족 못 하면 같은 query 를 refinement 와 함께 재호출 → multi-turn 길어짐. skill 본문에 "1회 호출 후 결과 그대로 사용" 강조. P3. + - **PR-Hermes-MaxTokens-Followup**: 23320 input tokens (31 tools + 90 skills + persona) → 30s+ first-token. tools/skills 선택적 로딩. P3. + +## 후속 트랙 (PR-1/2 user-facing E2E 진입 가능) + +| 우선순위 | 트랙 | 이전 진입 조건 충족? | +|---|---|---| +| **이제 가능** | PR-1 Layer 2/3 user-facing E2E 검증 (Discord smoke) | ✅ (Adapter A + env_passthrough) | +| **이제 가능** | PR-2 Layer 2/3 user-facing E2E 검증 (Discord smoke, web_search 자율 호출) | ✅ | +| 별 트랙 | PR-Hermes-Answer-Policy-1 (출처 라벨 plugin-level 강제) | PR-1/2 user-facing 안정화 후 | +| 별 트랙 | PR-Hermes-FamilyACL-N | 진입 조건 미정 | + +## 7일 안전망 (2026-05-24) + +- Mac mini: `~/.hermes/config.yaml.pre-env-passthrough.20260517` +- 복귀 시: `terminal.env_passthrough` 리스트 비우기 (또는 백업 복원) + +## 검증 commands (재실행) + +```bash +# Direct config verify +ssh macmini "~/.hermes/hermes-agent/venv/bin/python -c 'from tools.env_passthrough import is_env_passthrough; print(is_env_passthrough(\"HERMES_DOCSRV_TOKEN\"))'" + +# Hermes chat E2E (Discord 채널 입력으로 등가) +ssh macmini "HERMES_DOCSRV_TOKEN=... && ~/.hermes/hermes-agent/venv/bin/python -m hermes_cli.main chat -s docsrv_ask -q ''" + +# DS API 호출 확인 +ssh gpu "cd ~/Documents/code/hyungi_Document_Server && docker compose logs --since=5m fastapi | grep 'ask query'" +```