상세 컨텐츠

본문 제목

AI 에이전트 4종을 한 화면에서 보려고 로컬 관제 대시보드를 만들었다

개발

by 코드스키 2026. 7. 27. 15:09

본문

요즘 코딩 에이전트를 여러 개 동시에 쓴다. Codex, Claude Code, Cursor, Antigravity를 상황에 따라 갈아타는데, 문제는 지금 어느 세션이 일하는 중이고 어느 세션이 내 입력을 기다리는지 한눈에 안 보인다는 거였다. 창을 하나씩 열어서 확인하는 게 은근히 성가셔서, 이 네 개를 한 화면에 픽셀 사무실처럼 띄워주는 로컬 대시보드를 만들었다.

 

AI 에이전트 4종 로컬 관제 대시보드 대표 이미지
AI 에이전트 4종 로컬 관제 대시보드 대표 이미지

 

모델 호출도 훅도 없이, 로그만 읽는다

설계에서 처음 정한 원칙은 "읽기 전용"이었다. 각 에이전트한테 훅을 심거나 API를 호출하지 않는다. 대신 이 도구들이 로컬에 남기는 JSONL 세션 파일을 그냥 읽는다. Codex는 Codex대로, Claude Code는 Claude대로, Cursor와 Antigravity도 각자 세션 기록을 파일로 떨군다. 대시보드는 그 파일들을 주기적으로 파싱해서 각 세션의 현재 상태만 뽑아 픽셀 화면에 배치한다.

이 방식의 장점은 에이전트 동작에 전혀 개입하지 않는다는 거다. 모델을 부르지 않으니 토큰도 안 먹고, 훅이 없으니 에이전트가 느려지거나 깨질 일도 없다. 순수하게 관찰만 하는 계층이다. localhost의 작은 포트 하나에 웹 화면을 띄워서 브라우저로 본다.

 

Codex, Claude, Cursor, Antigravity 세션을 한 화면에 표시한 Agent Control Room 실행 화면
Codex, Claude, Cursor, Antigravity 세션을 한 화면에 표시한 Agent Control Room 실행 화면

 

working / waiting / done을 구분하는 게 핵심이자 함정

대시보드의 가치는 결국 상태 분류의 정확도에서 나온다. 세션이 셋 중 뭔지를 맞혀야 한다. 지금 실제로 작업 중(working)인지, 내 명령이나 승인을 기다리는(waiting) 상태인지, 아니면 끝난(done) 상태인지.

처음엔 이게 다 틀렸다. 끝난 세션도, 유휴 상태인 세션도 전부 "대기중(waiting)"으로 떴다. 원인을 파보니 각 도구의 세션 로그를 끝에서부터 읽어 마지막 이벤트로 상태를 판정하는데, 그 판정 조건이 엉성했다. 예를 들어 어떤 세션은 마지막 기록이 도구 호출(tool_use)에서 끊겨 있었다. 실제로는 작업이 끝났는데, 로그만 보면 "도구를 부르다 멈춘" 상태라 대시보드가 "아직 뭔가 하는 중" 또는 "대기 중"으로 오해한 거다.

그래서 각 도구별 파서의 분류 조건을 손봤다. 실제로 사용자 입력이나 권한 승인을 기다리는 interactive 상태만 waiting으로 잡고, 완료된 세션은 done으로 매핑했다. stop_reason이나 마지막 이벤트 타입을 도구마다 다르게 해석해야 해서, 도구별로 조건을 따로 뒀다.

tool_use에서 끊긴 트랜스크립트를 위한 idle 휴리스틱

그래도 남는 애매한 경우가 있었다. Cursor 세션이 tool_use로 끊긴 채 남아 있으면, 이게 진짜 지금 도구를 실행하는 중인지 아니면 그냥 그 상태로 끝나버린 건지 로그만으로는 구분이 안 됐다. 마지막 이벤트가 같으니까.

여기엔 시간 기반 휴리스틱을 넣었다. 마지막 이벤트로부터 일정 시간(나는 5분으로 뒀다)이 지나도록 아무 변화가 없으면 done으로 본다는 규칙이다. 대략 이런 형태다.

CURSOR_ACTIVE_IDLE_SEC = 300  # 5분

def classify_cursor_session(last_event, now):
    if last_event.type != "tool_use":
        return status_from_event(last_event)
    idle = now - last_event.timestamp
    if idle > CURSOR_ACTIVE_IDLE_SEC:
        return "done"   # tool_use에서 끊겼지만 충분히 오래 조용했다면 종료로 간주
    return "working"

코드에 CURSOR_ACTIVE_IDLE_SEC 같은 임계값을 두고, tool_use로 끊긴 트랜스크립트라도 그만큼 idle이면 완료로 처리했다. 이걸 넣고 나서야 실제로 도는 세션 하나만 working으로 남고 나머지는 제대로 done으로 정리됐다. 테스트 케이스를 몇 개 만들어서 이 분류가 흔들리지 않는지 확인했다.

로그인하면 알아서 떠 있게

마지막으로 이걸 매번 손으로 실행하는 게 귀찮아서 자동 실행을 걸었다. macOS라 launchd로 로그인 시 자동으로 서버가 뜨게 해두고, 자동 실행용 사본을 별도 위치에 뒀다. 이제 컴퓨터를 켜면 대시보드가 알아서 떠 있고, 브라우저 탭 하나로 네 에이전트의 상태를 훑는다.

 

Apple Developer 공식 문서 — launchd로 로그인 시 데몬/에이전트 자동 실행 설정
Apple Developer 공식 문서 — launchd로 로그인 시 데몬/에이전트 자동 실행 설정

 

만들고 나서 느낀 건, 이런 관제 도구의 어려움은 데이터를 모으는 게 아니라 "상태를 어떻게 정의하느냐"에 있다는 거였다. working과 done의 경계, waiting의 진짜 의미, 로그가 중간에 끊겼을 때의 처리. 결국 도메인(각 에이전트가 로그를 남기는 방식)을 정확히 이해해야 분류가 맞아떨어졌다.

참고

  • launchd로 macOS 로그인 시 자동 실행 등록
  • 각 에이전트 CLI의 로컬 세션 JSONL 구조(도구별 상이)

같이 읽기

여러 AI 도구의 역할과 비용 차이를 정리한 글은 ChatGPT Plus, GitHub Copilot, Google Colab Pro 뭐가 달라?에서 이어진다.

공식 자료

 

Claude Code 공식 문서 홈
Claude Code 공식 문서 홈

 

Codex 쪽 공식 문서

OpenAI Codex 공식 개발자 문서
OpenAI Codex 공식 개발자 문서

각 에이전트의 상태를 한 화면에 모으더라도, 지원 기능과 실행 모델은 공식 문서가 바뀔 때 함께 갱신해야 한다. 대시보드는 제품 기능을 대신 설명하는 게 아니라 로컬 프로세스 상태를 관찰하는 도구로 범위를 제한했다.

OpenAI Codex 문서

관련글 더보기