개요

OpenArms는 특정 에이전트 런타임을 하드코딩하지 않는다. 어댑터가 기동·입력 전달·출력 포착·결과 마커 인식을 할 수 있으면 어떤 CLI 에이전트든 워커로 붙는다. 핵심 원칙은 하나다 — 역할(role)이 안정적인 개념이고, 프로바이더(provider)는 그 역할을 채우는 교체 가능한 구현이다. 라이브 테스트베드는 지금 Hermes를 PM 역할에, Antigravity(AGY)를 워커 역할에 바인딩하지만, 이건 그 인스턴스의 바인딩이지 제품 기본값이 아니다. 같은 프로바이더가 어느 역할이든 채울 수 있고, 역할은 프로바이더 이름을 따라 개명되지 않는다.

이 장은 이 장이 건드리는 모든 도구/서비스를 — Hermes, AGY/Antigravity, tmux, Telethon, venv/uv, 그리고 그 사이의 전송(mailbox) — 왜 골랐는지·어떻게 구성하는지·배포판에서 훅을 어떻게 거는지까지 구체적으로 다룬다. 더 넓은 역할 모델은 핵심 개념, 설치 절차는 빠른 시작·설치, 어댑터를 직접 짜는 절차는 개발자 가이드를 본다.


Hermes — PM 프로바이더

왜 Hermes인가 (선택 근거)

Hermes CLI는 라이브 테스트베드에서 PM(코디네이터) 역할을 맡는다. 고른 이유는 Hermes가 공식 플러그인 훅 레이어를 노출하기 때문이다. 즉 OpenArms를 붙이려고 Hermes core를 패치하거나 사이드카 텔레그램 모니터를 띄울 필요가 없다 — 게이트웨이 디스패치와 CLI 입출력 지점에 콜백을 등록만 하면 된다. 대안(코어 포크, 외부 폴링 데몬)은 업스트림 업데이트마다 깨지고, 권위가 두 곳으로 갈라진다. 훅 등록 방식은 Hermes가 자기 업데이트를 받아도 브리지가 살아남는다.

어떻게 구성하는가 (스택)

Hermes 프로바이더가 소유하는 것:

  • 플러그인 브리지integrations/hermes/openarms_contract_bridge/. 공식 Hermes 플러그인으로 로드되는 훅 어댑터다. 매니페스트는 plugin.yaml, 훅 진입점은 __init__.py, 나머지는 attachment_sender.py·current_inbound.py· registry_bindings.py·thread_deletion_tools.py·thread_tool_registry.py 같은 헬퍼다.
  • PM 리스너(중립 코어)scripts/telegram_thread_router_listener.py. Hermes가 PM 프로바이더로서 구동하지만, 리스너 자체는 프로바이더 비의존이라 중립 코어에 남는다.

브리지는 레포 밖에 설치된다(예: ~/.hermes/hermes-agent/plugins/openarms_contract_bridge). 그래서 import openarms가 되려면 레포 루트가 sys.path에 있어야 한다. 브리지의 _bootstrap_openarms_root()는 먼저 OPENARMS_ROOT env를 보고, 없으면 ~/openarms 같은 알려진 체크아웃 위치와 자기 파일의 상위 디렉터리(openarms/를 실제로 담은 후보)까지 폴백해서 임포트를 복구한다. 이 폴백이 없으면 OPENARMS_ROOT 없이 리스폰된 세션이 Failed to load plugin 'openarms-contract-bridge': No module named 'openarms'로 죽고, 네이티브 openarms_thread_* 툴이 통째로 사라져 모델이 raw 터미널 CLI로 떨어진다.

plugin.yaml이 선언하는 계약:

name: openarms-contract-bridge
version: "0.2.0"
description: "OpenArms foreground contract bridge for Hermes official plugin hooks."
pip_dependencies:
  - telethon>=1.36
requires_env:
  - OPENARMS_ROOT
  - OPENARMS_HERMES_ROUTE_TARGET
  - OPENARMS_HERMES_ROUTE_EXECUTOR
  - OPENARMS_TMUX_BIN
hooks:
  - pre_gateway_dispatch
  - transform_llm_output
  - cli_input_before_submit
  - cli_response_after_render

env 표면은 OPENARMS_HERMES_* 접두로 갈래가 나뉜다 — 바이너리/프로세스 (OPENARMS_HERMES_BIN, ..._SESSION_ID), tmux 좌석(..._CLI_PANE, ..._GATEWAY_PANE, ..._FOREGROUND_SESSION), CLI 미러(..._CLI_INPUT_MIRROR, ..._CLI_RESPONSE_MIRROR), 라우팅/계약(..._ROUTE_TARGET, ..._ROUTE_LEDGER, ..._ROUTE_MAX_PENDING). 엔진이 역할 우선 명명으로 일반화되면 이 키들은 중립 OPENARMS_* PM-역할 키로 승계되지만, 오늘의 라이브 키는 OPENARMS_HERMES_*다.

배포판에서 훅을 어떻게 거는가

브리지는 Hermes의 공식 register(ctx) 진입점 하나로 자기를 꽂는다. 지원되지 않는 훅은 조용히 건너뛴다(_register_if_supportedregister_hook이 던지면 False를 반환). 짧은 예시:

HERMES_HOOK_PRE_GATEWAY_DISPATCH = "pre_gateway_dispatch"
HERMES_HOOK_TRANSFORM_LLM_OUTPUT = "transform_llm_output"
HERMES_HOOK_CLI_INPUT_BEFORE_SUBMIT = "cli_input_before_submit"
HERMES_HOOK_CLI_RESPONSE_AFTER_RENDER = "cli_response_after_render"
 
def register(ctx):
    _register_if_supported(ctx, HERMES_HOOK_PRE_GATEWAY_DISPATCH, pre_gateway_dispatch)
    _register_if_supported(ctx, HERMES_HOOK_TRANSFORM_LLM_OUTPUT, transform_llm_output)
    if _register_foreground_hooks_enabled():
        _register_if_supported(ctx, HERMES_HOOK_CLI_INPUT_BEFORE_SUBMIT, cli_input_before_submit)
        _register_if_supported(ctx, HERMES_HOOK_CLI_RESPONSE_AFTER_RENDER, cli_response_after_render)
    # 네이티브 thread 툴도 ctx.register_tool 로 등록 — LLM이 1급 툴로 선택
    _register_thread_tools(ctx)

각 훅의 역할:

  • pre_gateway_dispatch — 인바운드가 게이트웨이로 들어오기 전에 가로채 라우팅 결정과 현재 인바운드 스레드 컨텍스트를 세팅한다.
  • transform_llm_output / cli_response_after_render — 사람에게 보이기 전에 출력에서 내부 마커/raw id를 걷어내고 역할 헤더([PM]/[Worker])를 보장한다.
  • cli_input_before_submit — CLI에 제출되기 직전의 입력을 미러로 포착한다.

register_tool을 통해 등록되는 네이티브 thread 툴(openarms_thread_list, openarms_thread_delete_batch 등)은 Hermes의 기본 메시징/다이얼로그 목록(원본 사용자명과 topic #### 라벨을 그대로 덤프하는)을 대신해 LLM이 1급 툴로 고른다. 각 핸들러는 공개-안전 문자열(번호 목록·제목만)을 반환하고 openarms 내부를 지연 임포트해서 플러그인 임포트가 telethon에 하드 의존하지 않게 한다.


Antigravity (AGY) — 워커 프로바이더

왜 Antigravity인가

Antigravity CLI(agy)는 라이브에서 워커 역할을 맡는다. 고른 이유는 두 가지다. 첫째, AGY는 네이티브 첨부/파일 처리 표면워크스페이스 컨텍스트를 가져, 한 번에 들어온 한 장의 이미지를 한 번의 [PM] 턴으로 받아 일하는 워커 흐름에 맞는다. 둘째, AGY는 stdio JSON-RPC MCP 서버를 워커의 네이티브 스레드 어포던스로 붙일 수 있어, 워커가 PM과 동일한 툴-백드 증거(스레드 목록·삭제 계획)를 만든다.

어떻게 구성하는가

AGY 프로바이더가 소유하는 것:

  • 페르소나/플러그인integrations/antigravity/:
    • agy-global-AGENTS.md — 전역 워커 페르소나/규칙(대화 우선, 강한 툴 게이트).
    • openarms_provider/ — Antigravity 네이티브 플러그인 번들: plugin.json, commands/openarms-worker.md, hooks/openarms.json, skills/openarms-worker/SKILL.md.
  • 워커-스레드 MCPintegrations/antigravity/adapter/openarms_worker_threads_mcp.py. AGY env 키/agy.env/AGY 라우터 레지스트리를 해석하는 프로바이더 특정 stdio JSON-RPC MCP 서버. 기존 scripts/ 경로에는 얇은 re-export 심이 남아 라이브 mcp_config.json 진입점이 계속 해석된다.
  • 리스너/런타임scripts/agy_telegram_listener.py(유저의 AGY 채팅 메시지를 AGY foreground 페인으로 넣는 인바운드 브리지), scripts/agy_cli_to_telegram_mirror_once.py (완료된 한 턴을 final-only로 텔레그램에 1회 미러).

env 표면은 OPENARMS_AGY_*이고, 중립 승계 키는 OPENARMS_WORKER_*다(예: OPENARMS_WORKER_BOT_TOKENOPENARMS_AGY_TELEGRAM_BOT_TOKEN). fail-closed 게이트는 OPENARMS_AGY_TELEGRAM_CHAT_ID — 이 키가 없으면 AGY는 복구되지 않는다(과거 fail-closed 회귀의 가드).

배포판에서 훅을 어떻게 거는가

AGY는 선언적 훅 매니페스트(hooks/openarms.json)로 브리지를 푼다. 짧은 예시:

{
  "openarms_provider_bridge": {
    "role": "worker",
    "provider": "antigravity",
    "authority": "official_antigravity_cli_surface",
    "attachment_policy": "use_native_attachment_surface_before_fallback"
  }
}

읽는 법: 이 세션은 워커 역할이고, 권위는 공식 Antigravity CLI 표면(임의 스크래치 스크립트가 아니라)이며, 첨부는 폴백 전에 네이티브 첨부 표면을 먼저 쓴다. 워커 스킬 (skills/openarms-worker/SKILL.md)은 이 권위를 행동 규칙으로 강제한다 — 예를 들어 이미지 식별 요청은 자체 비전/CUA로 처리하지 않고 openarms-lens-identify CLI를 한 번 호출하며, 파일 전송은 응답 본문에 MEDIA:/절대경로 한 줄을 적어 엔진이 그 마커를 읽어 해당 텔레그램 스레드로 보내고 마커를 사람 답변에서 제거하게 한다.


전송(Transport) — 산출물 우편함

전송은 로컬 간 가져올 수 있는 산출물의 우편함이다. 워커 계약 레인도, 기억의 권위도, 정리 권한도 아니다 — ACK·REJECT·재시도·격리를 가진 메일박스다.

여기서 중요한 경계는 텔레그램 ≠ 워크플로 DB라는 것이다. 텔레그램 Bot API는 전송 (getUpdates/webhook으로 받고, sendMessage/sendPhoto/sendDocument로 보냄)이고, 들어온 업데이트는 봇이 받을 때까지만, 그것도 24시간을 넘기지 않고 보관된다. 그래서 PM 기억·워커 핸드오프·RAG/wiki 승격·감사 로그·장기 복구는 텔레그램이 아니라 OpenArms 원장/볼트가 권위다.

텔레그램 = 보이는 대화 표면 + Bot API 전송 + 제한된 앱 상태
OpenArms = 내구성 워크플로 상태 + 기억 볼트 + 증거 원장 + 전송 로그

브리지의 인바운드 컨텍스트는 runtime/hermes-pm-current-inbound-thread.json(또는 OPENARMS_HERMES_PM_CURRENT_THREAD_STATE 오버라이드)에 chat_id/message_thread_id/ reply_to_message_id로 박혀, 응답이 들어온 그 스레드로 정확히 돌아가게 한다.


tmux — foreground 실행석(execution seat)

왜 tmux인가

provider CLI(Hermes/AGY/Claude)는 사람이 보는 foreground 세션 안에 살아 있어야 한다 — 대화 컨텍스트, 인증 좌석, 화면 상태가 그 세션에 묶이기 때문이다. tmux는 그 좌석을 잡아두는 backend다. 고른 이유: 세션 지속성(터미널이 떨어져도 프로세스가 산다), 프로그래밍적 페인 제어(send-keys로 입력 주입), 출력 포착(capture-pane로 화면 읽기). 이 세 가지가 “CLI를 잡아두고, 입력을 넣고, 출력을 뽑는” 어댑터 계약과 정확히 맞는다.

중요: tmux는 제품 정체성이 아니라 현재 구현의 backend다. 로드맵은 execution_seat를 tmux/pty/ssh/windows-terminal/provider-native로 추상화하는 방향이다 — tmux를 코어에 굳히지 않는다. WSL/Linux/macOS/Mac VM에서 자연 동작하고, Windows native는 비주력이다.

어떻게 구성하는가

핵심 패턴은 detached 세션 + 명명이다:

# 좌석을 백그라운드로 만든다 (-d = 붙지 않고 생성, 이름 필수)
tmux new -s fleet -d
 
# 입력 주입: 대상은 session:window.pane
tmux send-keys -t fleet:openarms-pm.0 '안녕' Enter
 
# 화면 포착: 스크롤백 포함
tmux capture-pane -t fleet:openarms-pm.0 -p

OpenArms는 이 좌표(session:window.pane)를 env로 받는다 — OPENARMS_PM_PANE (기본 fleet:openarms-pm.0), OPENARMS_HERMES_CLI_PANE, OPENARMS_AGY_FOREGROUND_SESSION/OPENARMS_AGY_PANE 등. tmux 바이너리 자체는 OPENARMS_TMUX/OPENARMS_TMUX_BIN으로 가리킨다. 세션 이름은 항상 의미 있게 짓는다 — 무명 세션은 숫자 id(0,1,2…)로 떨어져 fleet 도구가 좌석을 잃는다.

배포판에서 훅을 어떻게 쓰는가

배포판의 주입 리스너가 이 패턴을 그대로 쓴다 — 인바운드를 받아 PM 페인으로 넣는 얇은 인젝터를 nohup으로 띄워 인터랙티브 세션에서 분리한다(짧은 예시):

export OPENARMS_PM_PANE="${OPENARMS_PM_PANE:-fleet:openarms-pm.0}"
export OPENARMS_TMUX="${OPENARMS_TMUX:-tmux}"
# 백로그 스킵 후 인젝터 기동 (decoupled)
pkill -f "openarms_provider/inject_listener.py" 2>/dev/null || true
nohup python3 "$REPO/integrations/claude_code/openarms_provider/inject_listener.py" \
  >> "$OPENARMS_HOME/logs/pm_injector.log" 2>&1 &
disown

리스너는 좌표 env를 읽어 send-keys로 텍스트를 페인에 넣고, 결과는 capture-pane(또는 프로바이더 미러 훅)으로 포착해 전송 우편함으로 되돌린다.


Telethon — Telegram MTProto 클라이언트

왜 Telethon(MTProto)인가, Bot API 대비

OpenArms의 현재 first messaging provider는 텔레그램이고, 두 경로가 있다 — HTTP Bot APIMTProto. 일반 전송(메시지/사진/문서)은 Bot API로 충분하다. 하지만 OpenArms가 하는 일 중 일부는 Bot API의 공개 표면을 넘는다:

  • 토픽/스레드 일괄 삭제·정리 — MTProto는 한 번에 최대 100개 메시지를 bulk delete/forward 한다. openarms_thread_delete_batch의 영구 삭제 의미가 여기서 나온다.
  • 큰 파일 — Bot API는 다운로드 20MiB/업로드 50MiB 한계인데, MTProto는 2GiB까지 된다.
  • 임의 메시지 id로 조회, owner 세션 수준의 토픽 열거 — 안전한 삭제 절차(열거 → 보고 → 명시 id 목록만 삭제)에 필요한 권한.

그래서 공개 전송은 Bot API, owner-runtime 토픽 조작은 MTProto(Telethon) 로 갈린다. Telethon은 순수 파이썬 asyncio MTProto 구현이라 셋업이 쉽고, 이벤트 구동·엔티티 해석이 에이전트 자동화에 잘 맞는다. (대안 TDLib은 C++ 바인딩이라 무겁다.)

⚠️ owner MTProto 세션은 강력하다 — 토픽 삭제는 반드시 열거→보고→명시 id 목록만 삭제로 진행하고, blanket sweep은 금지다(과거 over-delete 사고의 교훈).

어떻게 구성하는가

  • 의존성: telethon(필수). Hermes 브리지는 telethon>=1.36pip_dependencies로 선언한다. 단, 플러그인 임포트 경로는 telethon에 하드 의존하지 않게 내부를 지연 임포트한다 — telethon이 없는 환경에서도 플러그인 로드 자체는 살아 있어야 하기 때문.
  • 비밀값: bot token / chat destination / MTProto 세션은 owner-local secret이다. 레포에 커밋하지 않고 ~/.openarms/credentials/telegram.env 또는 .env로 둔다 (.env.example 참조). 배포 스크립트는 토큰을 출력하지 않고 자격 파일로 옮긴 뒤 권한을 600으로 잠근다.
  • flood control: MTProto는 사용자당 30 msg/sec 같은 flood 제한이 있으므로 미러/전송은 사이클당 전송 상한(..._CLI_MIRROR_MAX_SEND_PER_CYCLE)과 간격 (..._CLI_MIRROR_INTERVAL)으로 조절한다.

배포판에서 훅을 어떻게 쓰는가

브리지의 네이티브 thread 툴이 곧 MTProto 진입점이다. register_tool로 등록된 openarms_thread_list/openarms_thread_delete_batch/openarms_thread_send_attachment가 호출될 때만 telethon 내부를 지연 임포트한다. 따라서 배포판에서 훅 연동은 “telethon을 항상 임포트”가 아니라 “툴이 호출될 때 owner 세션을 연다”는 형태다. 텔레그램 표면의 더 넓은 설명은 메시징 플랫폼을 본다.


venv / uv — 격리 실행 환경

왜 격리 환경인가, 그리고 venv vs uv

OpenArms는 telethon·[jsonschema](https://json-schema.org/)·pytest 등 외부 의존성을 쓰고, 같은 호스트에 여러 provider venv(예: Windows의 hermes-agent venv)가 공존한다. 그래서 레포 전용 격리 환경이 필수다 — 호스트 전역 파이썬을 오염시키면 어느 인터프리터로 테스트가 도는지 모호해진다(실제로 Windows hermes venv에는 pytest/telethon이 없어 테스트 수집 자체가 안 된다).

  • venv — 파이썬 3.3+에 내장된 표준 격리. 추가 설치 없이 어디서나 되는 신뢰 기반. 공식 권위(pyproject.toml/requirements-dev.txt)를 그대로 쓴다.
  • uv — Astral의 Rust 기반 드롭인 대체(supersede)(pip/virtualenv/pip-tools를 대신). venv 생성이 훨씬 빠르고, 전역 캐시 재사용으로 공통 의존성을 즉시 설치하며, uv.lock으로 정확한 설치 버전을 고정해 재현성을 준다. 2026 신규 프로젝트의 기본 권장.

OpenArms는 둘 다 받는다 — 표준 보장은 venv로, 빠른/재현 설치는 uv로. 권위는 어디까지나 코드 메타데이터(pyproject.toml)다.

어떻게 구성하는가

표준(venv) 경로:

python -m venv .venv && source .venv/bin/activate   # Windows: .venv\Scripts\activate
pip install -e ".[dev]"
pytest -q

빠른(uv) 경로 — 같은 pyproject.toml을 읽는다:

uv venv && source .venv/bin/activate
uv pip install -e ".[dev]"

테스트는 WSL/Linux/macOS의 python3 로 레포 루트에서 돌린다. 한글 리터럴 출력이 깨지지 않게 PYTHONIOENCODING=utf-8을 둔다:

PYTHONIOENCODING=utf-8 python3 -m pytest -q

의존성 계층(권위는 pyproject.toml/requirements-dev.txt):

  • 필수 — 없으면 스위트가 안 돈다: pytest, telethon, jsonschema.
  • 권장 — 없으면 실패가 아니라 SKIP 되어 커버리지가 조용히 빠진다: [networkx](https://networkx.org/) (wiki_chain 그래프), pyyaml(node_contract 권위 렌더).
  • 선택 — 없어도 통과, 분기만 켜짐: leidenalg+python-igraph(community Leiden 분기), 래스터라이저 resvg_py/cairosvg(graph_network PNG 단계).

배포판에서 훅을 어떻게 쓰는가

배포(seat 설치) 스크립트는 격리 환경을 가정하고 동작한다 — 스킬 파일을 좌석의 ~/.claude/skills/로 복사하고, ~/.openarms를 bootstrap 한 뒤, 토큰을 ~/.openarms/credentials/telegram.env로 옮겨 chmod 600으로 잠근다(짧은 예시):

python3 openarms/paths.py bootstrap >/dev/null 2>&1
sed -E 's/^export //' "$TOK_SRC" > ~/.openarms/credentials/telegram.env
chmod 600 ~/.openarms/credentials/telegram.env

설치 전에 그 인터프리터로 한 번 preflight를 돌려 필수 의존성 PASS/MISSING을 확인한다 (import-only 검사, 설치/네트워크 없음):

python3 scripts/dev_preflight.py

CLI 에이전트 생태계 (현재·다음)

Hermes와 AGY/Antigravity가 현재 증명 프로바이더 한 쌍이다. 2026년 현재 터미널 네이티브 CLI 에이전트 생태계는 빠르게 넓어지고 있어 — Claude Code, OpenAI Codex, Google Antigravity(구 Gemini CLI 후속), 오픈소스의 OpenCode·Aider·Goose 등 — OpenArms의 중립 계약 모델은 이들 중 어댑터가 갖춰진 것을 워커로 받아들이는 것을 목표로 한다. 어댑터 작성 절차는 개발자 가이드를 본다.

연동은 모두 공식 CLI 계약으로 표현된다 — 어느 고용 세션이 권위인지, 위임된 계약 세션은 무엇인지, 입력을 어떻게 보내고 출력을 어떻게 포착하며, 사람에게 보이기 전에 터미널 잉크를 어떻게 걷어내고, 어떤 증거가 계약을 닫는지.


출처(코드/문서): integrations/README.md · integrations/hermes/ · integrations/antigravity/ · docs/20_installation/README.md · docs/30_providers/telegram/. 출처(외부 모범): HTTP Bot API vs MTProto — Telethon docs · MTProto vs HTTP Bot API (Telethon Wiki) · tmux(1) manual · Using environments — uv docs · awesome-cli-coding-agents · Best Terminal AI Coding Agents 2026 (amux)

0건의 항목