virtual-insanity
← 리포트 목록

Hermes 업그레이드 대비 — 로컬 커스텀 코드 기능 인벤토리 및 분류

2026-07-19 claude [hermes, migration]

Hermes 커스텀 코드 인벤토리 (v0.6.0 → origin/main 이사 대비)

  • 기준: ~/.hermes/hermes-agent 로컬 수정 196파일(+8047/-4928, 미커밋) + untracked 51파일 vs origin/main(2026-07-19, 13,114커밋 앞).
  • 방법: git diff 전수 훑기(공백 churn은 git diff -w로 판별) + untracked 파일 직접 읽기 + git show/grep origin/main:<path> 대조. 모든 "내장" 판정은 upstream 코드/문서 경로 근거 첨부.
  • 백업: ~/.hermes/backups/pre-upgrade-20260719/custom-tracked.patch, custom-untracked.tar.gz

0. 전체 집계

분류 기능 단위 수 핵심
내장 대체 가능 14 갓파일 분리 리팩터 전부, 웹 대시보드, 텔레그램 IP 폴백, 모델 카탈로그 등
부분 대체 (차이 있음) 6 shell 크론잡(운영 잡 133개 전부), codex 프록시, knowledge-room 차단 등
이식 필요 5 Harry 볼트 프리서치 세트, 채권 훅, 트윗/유튜브 자동 추출, lsof 폴백
폐기 가능 7 smart_model_routing 가드, PlatformAdapter 추상화, scaffolding, 공백 churn
미확인 4 아래 §5

최대 발견: 로컬 diff의 절반 이상(+8047 중 상당수, 196파일 중 약 120파일)은 공백/트레일링 화이트스페이스 정리로, 이식 대상이 아님. 실제 기능 커스텀은 위 32개 단위로 압축됨. 최대 리스크: 크론 잡 133개가 전부 로컬 커스텀 shell 잡 스키마 — upstream은 임의 command 문자열을 지원하지 않음(§2-1).


1. 내장 대체 가능 (14)

# 커스텀 기능 로컬 경로 upstream 근거 (origin/main) 비고
1 SSL 인증서 부트스트랩 gateway/_ssl_bootstrap.py (53줄, run.py에서 분리) gateway/run.py:1317 _ensure_ssl_certs (stale SSL_CERT_FILE 처리 추가된 개선판) + agent/ssl_guard.py, agent/ssl_verify.py, docs/rca-ssl-cacert-post-git-pull.md 로컬은 v0.6.0 코드의 위치 이동일 뿐
2 게이트웨이 내 크론 티커 + 하트비트 gateway/cron_ticker.py (86줄) cron/scheduler_provider.py InProcessCronScheduler (60s 티커) + cron/jobs.py:809 record_ticker_heartbeat / :840 get_ticker_heartbeat_age 하트비트 파일 경로 다름(로컬 ~/.hermes/cron/.heartbeat) — 외부 watchdog 경로 확인 필요
3 IterationBudget 분리 agent/agentic.py (102줄) agent/iteration_budget.py:17 class IterationBudget
4 SafeWriter/stdio 새니타이즈 분리 agent/session.py (123줄) agent/process_bootstrap.py, agent/conversation_loop.py (_install_safe_stdio/_SafeWriter grep 히트)
5 도구 병렬화·파괴명령 판정 분리 tools/manager.py (167줄) agent/tool_dispatch_helpers.py, agent/tool_executor.py (_should_parallelize_tool_batch/_is_destructive_command grep 히트)
6 CLI UI/워크트리 분리 hermes_cli/cli_ui.py, cli_worktree.py upstream cli.py+hermes_cli/_parser.py 등에 worktree 지원 + website/docs/user-guide/git-worktrees.md
7 통합 설정 로더 루트 config.py (73줄) + 7개 파일의 barrel import hermes_cli/config.py, hermes_cli/env_loader.py (upstream 자체 통합 로더 체계)
8 회전 로그 중앙화 루트 logging_config.py (136줄, env HERMES_LOG_MAX_MB/BACKUPS) hermes_logging.py (RotatingFileHandler + RedactingFormatter 중앙 모듈) env 변수명 동일 여부 미확인(§5)
9 웹 대시보드 web/(26파일) + hermes_cli/web_server.py(2,566줄) + web_dist/CLI 미등록 orphan 상태 upstream hermes_cli/web_server.py(18,274줄), web/(127파일), web/README.md(hermes dashboard 포트 9119), hermes_cli/main.py:12228+ 정식 등록, hermes_cli/dashboard_auth/ 로컬판은 upstream 초기 스냅샷의 부분집합. 로컬 전용 StatusPage/AgentsPage는 upstream SystemPage/SessionsPage/ChannelsPage가 커버(세부 §5)
10 텔레그램 API IP 차단 폴백 (repo 밖 hosts 고정 166.110→167.220 운영) plugins/platforms/telegram/telegram_network.py — DoH(구글/클플) 조회 + seed IP 149.154.166.110/167.220, SNI 보존 폴백 transport upstream이 프로그램적으로 해결 — hosts 고정 제거 가능
11 모델 카탈로그 gpt-5.5 범프 hermes_cli/models.py, codex_models.py upstream hermes_cli/codex_models.py:23 gpt-5.5, :55-58 gpt-5.6 계열까지
12 youtube-transcript-api 의존성 requirements.txt, pyproject.toml upstream pyproject.toml:267 youtube-transcript-api==1.2.4
13 auxiliary custom:<이름> 프로바이더 해석 (codex-proxy 연결용) agent/auxiliary_client.py (+39) upstream agent/auxiliary_client.py:4476-4485, 4927custom:<name>을 custom_providers에서 해석하는 로직 내장 custom:codex-proxy config 그대로 동작 예상
14 cron jobs.json 손상 항목 방어 (isinstance 가드) cron/jobs.py (+72) upstream cron/jobs.py:426 _normalize_job_record, :402 _coerce_job_text — 레코드 정규화 계층 방어 방식은 다르나 목적 동일

2. 부분 대체 — 차이 명시 (6)

# 커스텀 기능 로컬 경로 upstream 대응 차이 (이사 시 해야 할 일)
1 shell 크론잡 실행기command 문자열(shell=True)/cwd/env/timeout_seconds/silent/max_output_chars 지원. ~/.hermes/cron/jobs.json 운영 잡 133개 전부 이 스키마 cron/scheduler.py _run_shell_job, SCRIPT_JOB_KINDS={"script","shell","shell-job","shell_job"} cron/scheduler.py:2113 _run_job_script + cron/jobs.py:1072 create_job(script=, no_agent=True, workdir=) upstream은 ~/.hermes/scripts/ 내부 파일만 실행(경로 탈출 차단, .sh/.bash→bash, 그 외→python). 임의 command 문자열·env 오버레이·silent 마커 미지원. → 잡 133개를 scripts/ 스크립트로 변환하거나 _run_shell_job 커스텀 재이식. 스키마 필드(kind/type/job_type/command/cwd/silent)도 마이그레이션 필요
2 codex 프록시 (OpenAI chat/completions → ChatGPT Codex responses 변환, 127.0.0.1:18642) — LaunchAgent com.openclaw.codex-proxy.plist로 상시 구동 중, ~/.hermes/config.yaml 주 프로바이더(custom:codex-proxy)가 여기 연결 codex_chat_proxy.py (336줄, 스탠드얼론·repo 내 import 0건) Hermes 내부용: agent/codex_runtime.py(codex_responses api_mode·앱서버 네이티브), agent/codex_responses_adapter.py. 범용 프록시: hermes_cli/proxy/server.py(자격증명 포워더, 포맷 변환 없음, 어댑터는 nous_portal/xai뿐) Hermes 자신은 upstream 네이티브 codex 런타임으로 전환하면 프록시 불필요. 외부 18642 소비자(LLM 게이트웨이 폴백 경로)는 프록시 유지 필요 — 스탠드얼론이라 파일 복사만으로 유지 가능(이식 비용 0)
3 knowledge-room 차단 (멘션돼도 무시하는 chat_id 블록리스트, env TELEGRAM_BLOCKED_CHATS) gateway/platforms/telegram.py _telegram_blocked_chats (~12줄) plugins/platforms/telegram/adapter.py:916 fail-closed allowlist(기본 거부) + :7264 멘션 게이팅 + gateway/profile_routing.py(docs/profile-routing.md) upstream엔 "블록리스트" 개념이 없고 allowlist 역방향으로 표현 — 해당 방만 허용 목록에서 빼면 등가. 설정 재구성으로 해결(코드 이식 불필요)
4 /health에 크론 하트비트 나이 노출 (180s 초과 시 degraded) gateway/platforms/api_server.py (+13줄) 하트비트 인프라 내장(cron/jobs.py:840) + hermes cron status CLI 표시(hermes_cli/cron.py:256-266). HTTP /health 필드는 없음 외부 watchdog이 /health JSON을 보고 있다면 ~13줄 재이식, CLI로 갈아타면 불필요
5 스킬 슬래시커맨드 무조건 재스캔 (런타임 설치 스킬 즉시 인식) agent/skill_commands.py (+8) agent/skill_commands.py:416 get_skill_commands(플랫폼 스코프 변경 시 재스캔) + reload_skills()(/reload 커맨드) upstream은 자동 재스캔이 아니라 스코프 변경·명시적 reload 시에만 — 런타임 설치 즉시 반영을 원하면 8줄 재이식
6 오너 식별 (TELEGRAM_OWNER_ID env, settings.py get_owner_id + run.py _is_owner_chat) 루트 settings.py(41줄), gateway/run.py upstream에 "오너" 개념 없음 — TELEGRAM_ALLOWED_USERS/pairing(gateway/pairing.py)이 근접 개념 Harry 훅(§3-1)의 게이트 조건이므로 훅 이식 시 함께 이동. 단독으론 의미 없음

3. 이식 필요 (5)

# 커스텀 기능 로컬 경로 내용 이식 경로 제안
1 Harry 볼트 프리서치·구조화 응답 세트 (가장 큰 고유 커스텀) gateway/harry_helpers.py 486줄(14개 함수) + gateway/run.py 주입부(~90줄) ① GBrain(~/.bun/bin/gbrain)+~/knowledge+~/knowledge-agent 로컬 검색 결과를 매 턴 ephemeral 프롬프트에 주입(presearch) ② 오너 채팅에 로컬 자율성 프롬프트 추가 ③ 볼트 질의 시 강제 새 세션(force_new=True)+에이전트 캐시 무효화 ④ 오답 감지(*_is_bad/_is_ungrounded) 시 결정적 rescue 응답 대체(지식사랑방/KC 도메인 내용 하드코딩) upstream 대응물 전무(grep: presearch/gbrain/harry 0건). 재구현 메커니즘은 내장됨: plugins middleware llm_request (messages 재작성 공식 지원, docs/middleware/README.md) 또는 gateway/hooks.py(관찰 전용이라 주입엔 부적합). 텔레그램 어댑터가 plugins/platforms/telegram/adapter.py(9,416줄)로 이동했으므로 run.py 주입부는 새 구조에 맞춰 재작성
2 채권 답변 훅 (DM의 A/B/C/D 퀴즈 답변을 bond_answer_listener.classify_and_process로 분류·직접 응답, BOND_HOOK_NO_AUTOREPLY=1 중복 방지) gateway/platforms/telegram.py T1 (~60줄) ~/.hermes/workspace/scripts/pipeline/bond_answer_listener.py 연동 upstream 없음. 새 텔레그램 플러그인 adapter의 _handle_text_message 상당 위치에 재이식
3 트윗 본문·스레드 자동 추출 (fxtwitter/vxtwitter API 본문 조회, t.co 확장, 엔티티 링크 추출, 트리거어 시 nodriver 댓글 스크랩) gateway/platforms/telegram.py T2~T4 (~335줄) + gateway/browser_tools/tweet_replies_scraper.py (162줄, 스탠드얼론) env: HERMES_TWEET_REPLIES_MAX/ALWAYS, HERMES_TWEET_REPLY_PY. 스크래퍼는 /Users/ron/.openclaw/mcp-servers/stealth-browser/venv 파이썬 사용 upstream grep fxtwitter/vxtwitter 0건. 스크래퍼 파일은 복사만, 텔레그램 쪽 ~335줄은 플러그인 adapter에 재이식
4 유튜브 자막 자동 주입 (링크+요약 요청 감지 시 fetch_transcript.py 실행, 자막을 프롬프트 주입, 실패 시 환각 금지 지시) gateway/platforms/telegram.py T6 (~120줄) env HERMES_TELEGRAM_YOUTUBE_TRANSCRIPT_CHAR_LIMIT upstream엔 스킬(skills/media/youtube-content/scripts/fetch_transcript.py)만 있고 게이트웨이 자동 주입 없음 → 플러그인 adapter에 재이식
5 크론 상태 lsof 폴백 (macOS 관리 셸에서 ps aux 거부 시 포트 8642 lsof 프로브) hermes_cli/gateway.py (+34) upstream git grep lsof -- hermes_cli/ 0건 그대로 재이식 (~34줄)

참고: §2-1 shell 크론잡도 "변환 대신 이식"을 택하면 이 목록에 추가됨.


4. 폐기 가능 (7)

# 항목 로컬 경로 근거
1 smart_model_routing 한국어 복잡도 가드(+15) + 매 턴 라우팅 재읽기·로그 agent/smart_model_routing.py, gateway/run.py R7 upstream이 기능 자체를 제거: commit 424e9f36b "refactor: remove smart_model_routing feature (#12732)", agent/smart_model_routing.py 부재. cheap-routing이 없으니 오라우팅 방지 가드도 무의미
2 PlatformAdapter 형식 추상화(#22) — InboundMessage/OutboundMessage/ABC + 텔레그램 parse_inbound/send/verify_webhook gateway/platforms/base.py (+85), telegram.py T7 (~180줄) upstream이 자체 추상화 완성: 플랫폼은 plugins/platforms/<name>/adapter.py 플러그인 체계 + docs/relay-connector-contract.md(gateway/relay). 로컬 추상화는 병합 불가·불필요
3 hermes_agent/ 패키지 (errors/state 테스트-온리 scaffolding) + 전용 mypy strict 설정 hermes_agent/, pyproject.toml [tool.mypy] Phase-1 additive 계층, 프로덕션 소비자는 telemetry 1곳(mcp_serve.py)뿐. upstream은 hermes_logging.py/자체 구조로 진화
4 갓파일 분리 리팩터 그 자체 (#7/#9/#11/#13/#652: cli.py 776, run_agent.py 843, run.py 상당분) barrel import 구조 전반 목적이 코드 정리이며 upstream이 훨씬 크게 재구조화 완료(run.py 22,523줄 등). 기능 손실 없음 — 개별 기능은 §1에서 대체 확인
5 공백/트레일링 화이트스페이스 churn 196파일 중 약 120파일 (session.py/delivery.py/whatsapp.py/batch_runner.py/tools/* 등 git diff -w NOCHANGE 확인) 기능 0
6 .bak* 사이드카 51개 중 백업 파일 전부 *.bak-* 자동 편집 패스 잔재
7 .env.example 221줄 주석 카탈로그 .env.example 문서. 실 env 값은 ~/.hermes/.env에 있으므로 이식 불필요(참고용 보관만)

5. 미확인 (4)

# 항목 상태
1 HERMES_LOG_MAX_MB/HERMES_LOG_BACKUPS env가 upstream hermes_logging.py에서 같은 이름으로 지원되는지 미확인 — 변수명 다르면 로컬 크론 스크립트들의 env 설정 수정 필요
2 upstream 텔레그램 adapter의 message entities 링크 추출 유무 미확인 (fxtwitter 부재로 트윗 본문 기능 없음은 확정, 엔티티 URL 추출 자체는 별도 확인 안 함)
3 로컬 웹 StatusPage/AgentsPage 세부 항목이 upstream SystemPage 등에 100% 존재하는지 파일 목록 수준만 대조 — 기능 단위 상세 비교는 안 함
4 upstream drop_pending_updates 기본값이 로컬 커스텀(False=재시작 중 메시지 보존)과 일치하는지 adapter.py:2124에 파라미터화 확인, :2274 delete_webhook은 False — 폴링 시작 경로 기본값은 미확인

6. 이사 작업 우선순위 (이식 필요 목록 기준)

  1. 크론 잡 133개 마이그레이션 (§2-1) — 가장 큰 운영 리스크. 변환(command→scripts/ 파일) 또는 _run_shell_job 재이식 중 택일.
  2. Harry 프리서치·응답 세트 (§3-1) — harry_helpers.py 486줄 + run.py 주입부. middleware 플러그인으로 재구현 권장.
  3. 텔레그램 콘텐츠 훅 3종 (§3-2/3/4, 채권·트윗·유튜브) — 새 플러그인 adapter에 재이식 (~500줄).
  4. codex_chat_proxy.py 보존 (§2-2) — 스탠드얼론이라 복사만. LaunchAgent 경로(com.openclaw.codex-proxy.plist)가 repo 경로를 가리키므로 이사 후 경로 갱신 필요.
  5. 소형: lsof 폴백(34줄), /health 하트비트(13줄), 스킬 재스캔(8줄).
  6. 설정 재구성: blocked_chats→allowlist 역변환, hosts 고정 제거(upstream IP 폴백 사용), custom:codex-proxy config 유지 확인.