관리 메뉴

cyphen156

답답해서 만드는 에이전트 환경 동기화 프로젝트 #1 에이전트 대화 동기화하기 본문

프로젝트/AgentSessionSync

답답해서 만드는 에이전트 환경 동기화 프로젝트 #1 에이전트 대화 동기화하기

cyphen156 2026. 6. 20. 15:06

이 프로젝트는 전적으로 Codex와 Claude가 개발했습니다.

내가 한 역할은, 음…… 아이디어 제공하기?

그리고 4시간동안 Claude와 Codex 잘 갈구기!

이래서 딸깍 소리가 나오나 싶을 정도로 코드 한 줄 안 친 프로젝트입니다.

 

프로젝트의 목표는 단순하다.

Desktop과 Laptop 사이에서 에이전트 대화 세션이 끊김 없이 이어질 것.

클라우드 실행 환경을 사용하는 방법이 있다는 것은 알고 있다.

하지만 보안 문제도 있고, 결정적으로 에이전트를 계속 실행하면 사용량 토큰과 비용이 실시간으로 빠져나갈 것이 뻔했기 때문에 기각했다.

그렇다면 로컬 환경끼리 공유해야 한다.

문제는 Claude와 Codex의 대화 세션이 각 컴퓨터의 로컬 저장소에 격리되어 있다는 점이었다.

프로젝트 파일은 Git으로 옮길 수 있지만 대화 세션은 자동으로 따라오지 않는다. 같은 프로젝트를 다른 컴퓨터에서 열어도 이전 대화를 그대로 이어갈 수 없다.

초기안

처음 나온 방법은 다음과 같았다.

  1. GitHub를 통한 수동 Push & Pull 동기화
  2. 같은 네트워크 토폴로지에서의 원격 전송
  3. USB를 통한 물리적 전송

그런데 애초에 노트북과 데스크톱이라는 서로 다른 개발 환경을 사용한다는 것은 두 기기가 같은 장소나 네트워크에 없을 가능성이 높다는 뜻이다.

같은 네트워크가 필요하다면 결국 두 기기가 동시에 같은 공간에 있어야 한다.

그 정도면 USB를 꽂아서 물리적으로 옮기는 것과 크게 다르지 않다.

따라서 2번과 3번은 기각됐고, 최종적으로 1번이 채택되었다.

여기서 GitHub는 에이전트를 실행하는 클라우드 환경이 아니다.

인증정보와 개인 설정은 제외하고 대화 세션에 필요한 파일만 선택적으로 보관하는 중계 저장소로 사용한다.

작업이 끝난 컴퓨터에서 Push하고, 다른 컴퓨터에서 Pull한 뒤 같은 대화를 이어서 사용한다.

단순하게 말하면 이렇다.

대화 세션도 코드처럼 Git으로 옮기자.

무엇을 올릴 것인가

1번안도 그냥 파일을 Git에 올리는 것으로 끝나지 않았다.

먼저 무엇을 올릴 것인가부터 걸렀다.

Codex의 auth.json, API 키, 설정 DB까지 같이 올라가면 클라우드를 기각한 이유가 그대로 돌아온다.

그래서 처음에는 대화 세션 JSONL만 올리는 것으로 시작했다.

인증·설정·캐시는 .gitignore 화이트리스트로 차단했다. 올라가도 되는 파일만 허용하고 나머지는 전부 막는 방식이다.

물론 이것만으로 완전히 안전한 것은 아니다.

인증 파일을 제외해도 에이전트가 실행한 명령의 출력에 토큰이 찍혔다면 JSONL 본문에 그대로 들어갈 수 있다.

그래서 Push 직전에 세션 본문을 검사하고, 토큰으로 의심되는 문자열이 발견되면 업로드를 중단하도록 했다.

세션은 어디에 있는가

다음 문제는 실제 세션 저장 위치였다.

  • Claude: ~/.claude/projects/<작업경로 인코딩>/<UUID>.jsonl
  • Codex: ~/.codex/sessions/<날짜>/rollout-...-<UUID>.jsonl

여기서 첫 번째 함정이 나왔다.

Claude의 프로젝트 폴더 이름은 작업 디렉터리의 절대경로를 인코딩한 값이다.

C:\ClaudCode Project
→ C--ClaudCode-Project

즉 데스크톱에서도 작업 경로를 똑같이 맞추지 않으면 세션을 옮겨도 다른 프로젝트로 인식될 수 있다.

그래서 양쪽 컴퓨터의 작업 경로를 C:\ClaudCode Project로 통일했다.

여기까지 하고 이제 되겠지 싶었다.

안 됐다.

파일은 있는데 앱에는 없다

세션 파일은 분명 데스크톱에 복사되어 있었다.

터미널에서 다음 명령을 실행하면 대화도 정상적으로 열렸다.

claude --resume <UUID>

데이터는 멀쩡하다는 뜻이다.

그런데 Claude 앱의 대화 목록은 비어 있었다.

실제 저장 파일을 뒤져서 확인한 결과, 현재 사용 중인 Claude 앱은 세션 본문 외에 claude-code-sessions라는 별도 목록 레지스트리를 사용하고 있었다.

본문 JSONL만 옮기고 앱 레지스트리를 옮기지 않았으니 CLI에서는 열리지만 앱 목록에는 나타나지 않았던 것이다.

더 귀찮은 점은 일반 설치판과 Microsoft Store 설치판의 저장 위치도 달랐다는 것이다.

한쪽은 %APPDATA%, 다른 쪽은 %LOCALAPPDATA%\Packages\... 아래에 데이터를 저장하고 있었다.

같은 Claude 앱인데 설치 방식에 따라 경로가 달랐다.

두 경로를 자동 탐지해 레지스트리를 함께 복사하도록 수정하고 앱을 완전히 재시작하자 대화 6개가 목록에 그대로 나타났다.

이때부터 단순한 세션 복사가 아니라 환경 동기화가 되기 시작했다.

대화 본문만 옮겨서는 부족했다.

앱이 그 대화를 인식하는 메타데이터도 같이 가야 했다.

Codex는 비슷하지만 같지는 않았다

Codex는 대화 본문을 sessions/ 아래 rollout JSONL로 저장하고, session_index.jsonl에는 대화 UUID와 제목을 기록한다.

처음에는 이 인덱스가 앱 목록 자체라고 판단했다.

그런데 실제로 확인해보니 아니었다.

Codex 앱은 rollout JSONL과 로컬 상태 DB를 함께 사용한다. 다른 컴퓨터에서 가져온 rollout이 있으면 앱이 이를 다시 스캔해 목록 정보를 복구할 수 있고, session_index.jsonl은 대화 제목을 보완하는 역할에 가깝다.

실제로 데스크톱에서 만든 Codex 세션을 노트북으로 옮겨봤다.

해당 UUID는 노트북의 session_index.jsonl에 없었지만, rollout JSONL을 복원하자 현재 실행 중인 Codex 앱 목록에 바로 나타났다.

앱 재시작도 필요 없었다.

그래도 제목 정보까지 최대한 유지하기 위해 session_index.jsonl도 동기화했다.

문제는 이 파일이 양쪽 컴퓨터에서 함께 수정되는 단일 파일이라는 점이었다.

한쪽 파일로 덮어쓰면 다른 컴퓨터에서 만든 제목 정보가 사라질 수 있다.

그래서 UUID를 기준으로 양쪽 항목을 합치고, 같은 UUID가 있으면 더 최근의 updated_at을 남기는 union 머지를 적용했다.

state_5.sqlite는 동기화하지 않았다.

실제 Codex 동작과 공식 소스를 확인한 결과, rollout JSONL을 통해 로컬 DB를 다시 구성할 수 있었다. 굳이 컴퓨터별 상태 DB까지 Git에 올릴 이유가 없었다.

안전장치가 오히려 방해했다

마지막 문제는 사람이 작업 종료를 깜빡하는 경우였다.

처음에는 한쪽 컴퓨터가 baton을 잡으면 Finish하기 전까지 다른 컴퓨터의 Start를 막도록 설계했다.

안전하기는 했다.

그런데 실제로 사용해보니 Finish를 한 번 빼먹는 순간 다른 컴퓨터에서 아무것도 못 하는 구조였다.

결과는 그냥 이것이었다.

왜 안 되지?

그래서 완전 차단 대신 경고 후 합류하는 방식으로 바꿨다.

서로 다른 새 대화는 UUID가 다르기 때문에 자연스럽게 합쳐진다.

다만 양쪽 컴퓨터에서 같은 UUID의 대화를 동시에 진행하면 같은 JSONL이 서로 다른 방향으로 변경될 수 있다. 이 경우까지 자동으로 의미 있게 합치는 것은 불가능하다.

결국 사용 규칙은 하나 남았다.

컴퓨터를 옮길 때는 Finish하고, 다른 컴퓨터에서는 Start한다.

깜빡했다고 시스템 전체가 멈추지는 않는다.

하지만 같은 대화를 두 컴퓨터에서 동시에 진행하는 것까지 허용한 것은 아니다.

실행 중인 에이전트를 어떻게 종료할 것인가

여기서 또 문제가 생겼다.

Finish 스크립트는 안전을 위해 Codex와 Claude가 완전히 종료된 경우에만 세션을 복사하도록 되어 있었다.

그런데 Codex에게 Finish 스크립트를 실행시키면 Codex가 실행 중인 것은 당연하다.

자기 자신이 살아 있어서 자기 세션을 저장하지 못하는 구조였다.

그래서 종료 조건을 없앴다.

Codex와 Claude의 세션 JSONL은 append-only 형태이므로 현재까지 기록된 내용을 스냅숏으로 복사하고, 마지막 줄이 완전한 JSON인지 검사한 뒤 Push하도록 변경했다.

기록 중간에서 파일이 잘렸다면 업로드하지 않는다.

덕분에 이제 에이전트를 종료하지 않고도 Finish를 실행할 수 있다.

최종 구조

결과적으로 저장소는 두 개로 나뉘었다.

MultiAgentCrossReview

Codex와 Claude가 같은 질문에 독립적으로 답하고 서로의 결론을 교차검증하는 공개 프로젝트다.

질문, 초기 판단, 반박, 수정 결론, 증거 확인, 사용자 Callback과 최종 결정을 기록한다.

AgentSessionSync

노트북과 데스크톱 사이에서 실제 Claude·Codex 대화 세션을 운반하는 비공개 저장소다.

포함되는 것은 다음과 같다.

  • Claude 세션 JSONL
  • Codex rollout JSONL
  • Claude 앱 목록 레지스트리
  • Codex 제목 인덱스
  • 시크릿 검사기
  • Push/Pull 및 Start/Finish 스크립트

사용 방법은 두 번의 클릭으로 끝난다.

작업 시작
→ 1_Start-Work.cmd

작업 종료
→ 2_Finish-Work.cmd

결론

이 모든 것을 “될 것 같다”로 끝내지 않았다.

실제 파일을 열어보고, 양쪽 컴퓨터에서 직접 Push와 Pull을 수행하고, 앱 목록에 대화가 나타나는지 확인했다.

Codex와 Claude가 서로 자기 진단이 맞다고 주장할 때도 판정 기준은 하나였다.

그래서 실제 화면에 뜨는가?

추측은 자주 틀렸다.

파일을 직접 세고, 저장 경로를 확인하고, 실제 UUID를 옮겨보는 쪽이 항상 더 빨랐다.

그렇게 단순한 불편 하나가 저장소 두 개와 적지 않은 삽질로 끝났다.

코드는 한 줄도 직접 작성하지 않았다.

대신 안 되는 것을 계속 안 된다고 말했다.

어쩌면 지금 시점에서 사람이 해야 하는 개발은 이런 것인지도 모르겠다.

이제 내가 해야하는것은

1. 대화 세션 시작하기 전에 Start 부팅에 해당하는 cmd 파일 실행해서 동기화 실행하기 

2. 대화 끝나고 finish에 해당하는 cmd 파일 실행해서 동기화용 데이터 업로드하기 

아래는 실제로 대화가 공유됨을 증명한 스크린샷 들입니다.

Codex DeskTop - LabTop

ClaudeCode DeskTop - LabTop

 

 

 

 

 

※ 추가 내용 

웃긴 점은 이 동기화 환경을 개발하는 동안에는 아직 동기화 프로젝트가 완성되지 않았다는 것이다.

그래서 개발 중 필요한 대화 전달은 ChatGPT 웹을 중간 통로로 사용했다. 한쪽 환경에서 나온 결과를 웹 대화에 전달하고, 다른 환경에서 다시 확인하는 방식이었다.

한마디로 말하면 이렇다.

에이전트 환경 동기화 프로젝트를 만들기 위해, 완성되기 전까지는 사람이 직접 대화를 중계했다.

꼼수 부린 거다. ㅋㅋㅋㅋㅋㅋㅋㅋ