Claude Code 서브에이전트 폭주를 사전에 막는 플러그인
Claude Code에서 서브에이전트를 여러 개 띄우는 순간 5시간 사용량 윈도가 몇 분 만에 증발하는 경험을 했다면, agent-usage-guard는 그 지출이 실제로 발생하기 전에 개입하는 플러그인이다. 별도 프로세스나 네트워크 연결 없이 Claude Code 내부의 훅으로 동작하며, 플랜이 보고하는 실시간 사용률과 이 머신에서 벌어지고 있는 모든 세션의 에이전트 수를 판단 근거로 삼는다. 사용이 끝난 뒤에 알려주는 것이 아니라, 에이전트가 시작되기 전·프롬프트가 전송되기 전·도구 호출이 실행되기 전에 멈춰 세운다는 점이 핵심이다.
설치는 Claude Code v2.1.287 이상에서 마켓플레이스 명령 두 줄이면 끝난다. `/plugin marketplace add rbartoli/agent-usage-guard`로 저장소를 등록하고 `/plugin install agent-usage-guard@agent-usage-guard`로 설치한 뒤 `/agent-guard`를 실행하면 현재 가드가 무엇을 보고 있는지 확인할 수 있다. `/agent-guard status`, `allow`, `pause`, `resume`, `report`, `help` 같은 하위 명령이 제공되며, 이 명령들은 턴 중간에도 즉시 실행되고 모델 턴을 새로 열지 않기 때문에 Claude가 이를 권한 허용으로 오해할 여지가 없다. 동작은 Claude Code v2.1.295 기준으로 검증됐다.
가드가 붙잡는 지점은 한두 가지가 아니다. 5시간 윈도가 특정 수준에 도달하면 새 에이전트에 승인을 요구하고, 더 높은 임계값에서는 아예 거부한다. 윈도 사용률이 짧은 시간에 급격히 오를 때도 승인을 요구한다. 이 머신 전체를 기준으로 동시에 도는 에이전트 수, 10분당 시작·재개되는 에이전트 수, 10분당 서브에이전트가 처리하는 토큰량에도 상한이 걸린다. 서브에이전트가 자기 아래에 또 에이전트를 만드는 행위는 차단되고, 큰 컨텍스트를 다시 읽어야 하는 프롬프트는 압축 후 전송·그대로 전송·취소 중 하나를 고르게 한다. 컨텍스트가 무거운 상태에서의 10분당 도구 호출 수, 프롬프트 캐시가 만료된 무거운 세션의 재개, 같은 도구 호출이 다른 호출 없이 반복 실패하는 상황도 각각 별도 게이트로 다뤄진다.
이 도구가 나온 배경에는 작성자 자신의 실패 기록이 있다. 어느 오후 최대 노력(max effort) 설정으로 연구용 프롬프트를 하나 던졌더니 서브에이전트 호출 394건이 발생했고 그중 341건이 동시에 돌면서 10분 만에 8,400만 컨텍스트 토큰이 API를 통과했다. 결과는 사용량 한도 도달과 윈도 리셋까지의 잠금이었다. 60일치 로그를 모아보니 잠금 12회, 에이전트 버스트가 발생한 10분 구간 166개가 잡혔다. 큰 컨텍스트, 빠른 도구 루프, 병렬 에이전트, 눈먼 재시도가 곱해지는 구조가 문제였고, 가드의 각 보호 장치는 이 곱셈의 인자를 하나씩 제거하는 방식으로 설계됐다. 단일 프롬프트에서 서브에이전트 339건이 생성되거나, 재귀적 생성으로 주간 Max 플랜의 절반이 소진되거나, 멀티에이전트 실행이 일반 채팅 세션의 약 15배 토큰을 쓰는 사례도 함께 언급된다.
사람이 키보드 앞에 있을 때 가드는 Claude Code가 원래 쓰는 질문 대화상자를 그대로 빌려 묻는다. 답이 올 때까지 해당 호출은 대기하고, 그사이 병렬로 시작된 에이전트는 보류됐다가 응답이 오면 다시 출발한다. 한 번의 승인은 조건이 해소되거나 타임아웃될 때까지 유지되므로 같은 질문이 호출마다 반복되지 않는다. 플랜 윈도는 리셋까지, 무거운 컨텍스트는 크기가 줄어들 때까지(도구 호출의 경우 최대 1시간), 에이전트 카운트는 10분간 승인이 유지된다. 반대로 거절하거나 대화상자를 닫으면 이후 10분간 에이전트 시작이 막히고 그동안 재질문도 하지 않는다. 입력한 문장은 Claude에게 그대로 전달되기 때문에 "use haiku agents instead"처럼 대안을 지시하는 것도 가능하다.
사람이 없을 때의 동작은 다르다. `claude -p`, Agent SDK, dontAsk 모드에서는 묻지 않고 거부하며, 거부 사유가 도구 결과로 모델에 전달된다. 모델은 그 이유를 읽고 작업을 모아서 처리하거나 기다리거나 멈출 수 있다. 같은 조건이 반복 거부되면 문구가 매번 달라지며 단계적으로 강해진다. 두 번째에는 상황이 달라지지 않았으니 다른 방법을 시도하라고 하고, 세 번째에는 턴을 끝내라고 요구한다. `/goal` 같은 Stop 훅이 턴을 계속 다시 열면 사용자가 직접 결정해야 한다는 취지의 거부가 나간다.
프라이버시 설계는 의도적으로 최소한이다. 네트워크도, 별도 프로세스도 쓰지 않고 카운트와 거친 구간 버킷만 저장한다. 프롬프트 본문, 도구 입력, 모델 출력은 기록하지 않는다. 훅 자체가 실패하면 해당 이벤트는 그냥 진행된다. 고장 난 가드가 세션을 망가뜨리지 않도록 한 fail-open 정책이며, 유일한 예외는 제때 판단하지 못한 에이전트로, 이 경우는 거부해서 Claude가 다시 시작하도록 만든다.
실무적으로 달라지는 지점은 사용량 관리의 위치다. 지금까지는 한도에 부딪힌 뒤 기다리는 수동적 대응이었다면, 이 플러그인은 머신 단위의 로컬 정책으로 멀티에이전트 루프의 상한을 미리 정해둔다. Claude Code 자체에도 동시 서브에이전트 20개, 중첩 3단계 같은 네이티브 제한이 있지만 세션이 시작할 수 있는 에이전트 총량에는 상한이 없다. 가드의 기본값은 그보다 엄격하게 잡혀 있다. v2.1.234 이후 Claude Code는 사용량 한도에 닿으면 스스로 대기했다가 리셋 시점에 이어가므로, 가드의 역할은 한도 도달 자체를 피하게 만드는 쪽으로 옮겨갔다.
한계도 분명하다. 각자 터미널 패널에서 도는 팀메이트는 다른 프로세스에서 루프가 돌기 때문에 보이지 않는다. API가 직접 실행하는 도구는 도구 이벤트가 발생하지 않아 붙잡을 수 없다. `/subtask` 포크처럼 agent.spawn 없이 시작되는 작업은 시작 시점에 카운트만 될 뿐 사전 차단되지 않는다. API 키 세션은 플랜 사용률을 보고하지 않으므로 토큰·횟수 예산만 적용된다. 데스크톱 앱의 WSL 세션에서는 모드가 동작하지 않는다. 백그라운드 작업 알림이나 다른 세션의 메시지는 결과가 유실될 수 있어 건드리지 않는다.