에이전트 세션을 핸드오프 해보기 #1

🗓️

Claude Code를 핸드폰에서 이어 쓰고 싶었을 뿐인데

누가 클로드 세션을 가지고 있을까?

Claude Code 세션을 모바일로 넘겼다가 다시 개발 PC로 가져오려면 무엇이 필요한가. Orca와 Paseo를 구조부터 뜯어보고, Paseo가 만든 세션이 정말 native Claude Code로 돌아오는지 내 개발 PC에서 직접 확인했다.

나는 Warp + Claude Code로 개발하고 있다. 프로젝트 디렉터리로 가서 claude를 띄우고, 하다 만 작업은 다음에 같은 디렉터리에서 resume한다.

이 환경에 큰 불만은 없다. Warp의 터미널 UX도 좋고 Claude Code도 안정적이다. 프로젝트별 CLAUDE.md, skills, MCP 설정도 이미 Claude Code를 중심으로 맞춰놨다.

문제는 하나였다. 밖에 나가면 이 환경과 완전히 단절된다.

Claude에게 20분짜리 작업을 시켜놓고 자리를 비웠다고 하자. 밖에서 문득 결과가 궁금해진다. 잘 끝났으면 테스트 하나만 더 시키고 싶다. Claude가 중간에 질문을 던지고 멈춰 있으면 답도 해주고 싶다. 아니면 아예 다른 작업을 하나 더 던지고 싶다.

그런데 그 작업 단위는 내 개발 PC에서 돌고 있다.

처음에는 단순한 원격 터미널 문제라고 생각했다. 조금 파보니 그게 아니었다.

이미 한 번 겪은 일이다

Claude Code를 쓰기 전에는 OpenCode를 잘 썼다. 여러 모델을 한 에이전트 환경에서 다룰 수 있었고 Anthropic 구독을 연동해 Claude 모델을 쓰는 방식도 편했다.

그러다 Anthropic이 외부 에이전트가 Claude 구독 인증을 쓰는 것에 제동을 걸면서 상황이 달라졌다.

여기서 중요한 건 누가 옳고 그르냐가 아니다. 외부 제품이 Claude의 인증이나 실행 계층에 의존하면, 정책 변경 한 번으로 사용 방식 전체가 바뀔 수 있다는 걸 겪었다는 것이다.

그래서 가장 단순한 형태로 돌아왔다. Warp는 터미널이고, 실제 에이전트 역할은 그 안에서 도는 공식 claude 프로세스가 한다. Claude Code가 Anthropic에 인증하고, tool을 실행하고, 세션을 관리한다. 대화 기록도 ~/.claude 아래에 자기 형식으로 저장한다.

Warp가 그 세션을 소유하지는 않는다. 그래서 내일 Warp를 지워도 아무 일도 일어나지 않는다. iTerm을 켜서 claude를 실행하면 그만이다. 기존 세션도 /resume이나 claude --resume으로 다시 부른다.

지금 내 환경의 system of record는 Warp가 아니라 Claude Code다.

이 구조가 생각보다 중요했다. 모바일을 붙일 때 조건이 하나 생겼기 때문이다. 모바일 때문에 잘 돌아가는 Claude Code 중심 구조까지 바꾸고 싶지는 않다.

내가 원한 건 모바일 Claude가 아니었다

처음에는 핸드폰에서 Claude Code를 쓸 수 있으면 된다고 생각했다. 원하는 상황을 하나씩 적어보니 요구사항이 좀 달랐다.

개발 PC에서 ~/workspace/service-a에 세션 A를 띄워 “주문 API 리팩터링”을 시켜놓고 자리를 뜬다. 핸드폰을 꺼내 A의 진행 상태를 본다. Claude가 질문하면 답한다. 끝났으면 추가 작업을 시킨다.

여기까지도 부족하다. 밖에서 갑자기 다른 생각이 날 수 있다. 이 Redis 캐시 구조도 한번 분석시켜볼까. 그러면 A에게 일을 더 시키는 게 아니라 새 세션 B를 띄우고 싶다.

그리고 다시 개발 PC 앞에 앉는다. 여기서 A와 B를 평소 쓰던 Claude Code로 그대로 이어서 쓰고 싶다.

사실 “세션을 이어갈 수 있느냐”만 놓고 보면 이미 해결된 문제다. 세션은 로컬에 저장되고 claude --resume이 있으니, 핸드폰에서 SSH로 개발 PC에 들어갈 수만 있으면 된다.

문제는 “가능하다”와 “자연스럽다” 사이의 거리다. 매번 VPN을 켜고, SSH 클라이언트를 열고, 디렉터리로 이동하고, 세션 목록에서 하나를 찾아 resume하는 건 내가 원한 게 아니다.

세션을 이어가는 게 아니라, 세션이 이미 이어져 있는 것처럼 보이는 것. 이쪽이다.

그래서 Orca와 Paseo를 볼 때도 원격 접속이 되느냐는 중요하지 않았다. Claude Code가 이미 제공하는 세션 연속성 위에 어떤 경험을 얹느냐가 중요했다.

세션은 누가 만드는가

Orca와 Paseo는 화면만 보면 비슷하다. 둘 다 Claude Code나 Codex 같은 에이전트를 여러 개 띄우고, 프로젝트별 workspace를 관리하고, 모바일에서도 상태를 본다.

그런데 Claude를 다루는 방식이 꽤 다르다. 그리고 그 차이가 내가 가장 중요하게 본 세션 소유권과 직접 연결돼 있었다.

Orca

Orca는 자기를 IDE가 아니라 ADE(Agent Development Environment)라고 부른다. 프로젝트 하나에서 여러 작업을 동시에 돌리면, 각각 별도의 git worktree와 터미널을 만들고 그 안에서 CLI 에이전트를 실행한다.

핵심은 문서에 적힌 이 한 줄이다.

any CLI agent — if it runs in a terminal, it runs in Orca

터미널에서 돌면 Orca에서도 돈다. 결국 실행되는 건 공식 claude 프로세스다. 그러니 인증, 설정, skills, MCP, 세션은 전부 Claude Code 영역에 남는다.

내가 쓰던 Warp → PTY → claude와 구조가 거의 같다. Warp 자리에 에이전트 관리에 특화된 환경이 하나 들어갔을 뿐이다.

Paseo

Paseo는 다르다. 중심에 Paseo 데몬이 있고 Desktop, Mobile, Web, CLI가 전부 그 데몬을 바라보는 클라이언트다. 데몬은 workspace와 worktree, 에이전트 생명주기, 부모/자식 관계, 실행 상태와 timeline, permission 요청, 터미널, orchestration tool을 직접 관리한다.

orchestration tool을 에이전트에게 MCP나 provider-native tool로 주입할 수도 있다. Claude에게 “이 문제를 세 개의 subagent로 나눠서 조사해”라고 시키면, Claude가 Paseo의 tool을 통해 다른 에이전트를 만들고 제어하는 것도 가능하다.

그리고 Claude만 좀 특별하다. Paseo의 일반적인 provider 모델은 설치된 CLI를 subprocess로 띄워 supervise하는 방식인데, Claude 어댑터는 그렇지 않다. 저장소를 열어보면 바로 나온다.

// packages/server/src/server/agent/providers/claude/query.ts
import { query, type Options, type Query, type SpawnOptions } from "@anthropic-ai/claude-agent-sdk";

PTY 안에서 claude를 띄워 화면을 긁는 게 아니라 @anthropic-ai/claude-agent-sdkquery()를 직접 쓴다.

참고로 Paseo는 provider 어댑터를 ACP(Agent Client Protocol)로 옮기는 RFC를 이미 완료 처리했다. 지금 저장소에는 Copilot, Cursor, Kimi, Kiro, Trae용 ACP 어댑터가 들어와 있다. 그런데 Claude, Codex, OpenCode는 아직 각자의 전용 어댑터를 쓴다. Claude는 여전히 Agent SDK 위에 있다.

여기까지 보고 걱정이 생겼다. 그러면 Paseo에서 만든 세션은 Paseo 안에서만 쓸 수 있는 것 아닌가?

Paseo 세션은 Claude Code로 돌아오는가

이 부분이 이번 조사에서 가장 의외였다. 마침 개발 PC에 Paseo가 깔려 있어서 문서 대신 디스크를 직접 봤다.

Paseo 에이전트는 ~/.paseo/agents/<프로젝트>/<agent-id>.json에 저장된다. 열어보면 runtimeInfo가 있다.

$ jq -c '.runtimeInfo' ~/.paseo/agents/Users-dev-workspace-my-project/9d4e1c77-*.json
{"provider":"claude","sessionId":"7f3c9a21-4b8e-4d16-9c05-2e71a8d3f640","model":null,"modeId":"auto"}

sessionId가 있다. 이 ID로 Claude 쪽을 찾아봤다.

$ find ~/.claude/projects -name '7f3c9a21-*.jsonl'
/Users/dev/.claude/projects/-Users-dev-workspace-my-project/7f3c9a21-4b8e-4d16-9c05-2e71a8d3f640.jsonl

있다. Paseo가 Agent SDK로 만든 세션도 결국 Claude가 다시 읽을 수 있는 transcript를 남긴다. Paseo 전용 포맷으로 가둬놓고 export를 시켜주는 구조가 아니다. 애초에 underlying 세션이 그 자리에 있다.

반대 방향도 열려 있다. 기존 Claude 세션을 Paseo로 가져오는 명령이 CLI에 있다.

paseo import <session-id> --provider claude --cwd <path>

그러니 데이터 모델상 왕복은 막혀 있지 않다. 여기까지는 예상보다 좋았다.

그런데 고리가 두 군데 끊겨 있다

첫째, 세션 ID를 CLI로 꺼낼 수 없다.

paseo inspect <id> --json이 돌려주는 필드는 Id, Name, Provider, Model, Thinking, Status, Archived, Mode, Cwd, CreatedAt, UpdatedAt, LastUsage, Capabilities, Worktree, ParentAgentId다. paseo ls --json도 마찬가지다. 둘 다 sessionId가 없다.

값은 이미 메모리에도 있고 디스크에도 있는데 사용자에게 나오는 경로만 없다. 그래서 위에서 내가 한 것처럼 ~/.paseo/agents/*.json을 직접 뒤져야 한다. 내부를 손으로 헤집는 느낌이 든다.

이걸 요청한 이슈가 실제로 있었다. #493 — expose Claude sessionId (or add paseo resume)이고 지금 Closed다. 다만 구현돼서 닫힌 게 아니다. Paseo는 GitHub Issues를 버그에만 쓰고 기능 요청은 Discussions나 Discord로 받는다. 그 정책에 따라 intake가 닫혔다.

둘째, 이 transcript는 cli가 아니라 sdk-cli로 찍힌다.

Claude는 transcript 각 줄에 어떤 경로로 만들어진 세션인지를 남긴다. 내 개발 PC에 쌓인 걸 세보면 이렇다.

$ grep -ho '"entrypoint":"[^"]*"' ~/.claude/projects/*/*.jsonl | sort | uniq -c | sort -rn
437745 "entrypoint":"cli"
  3047 "entrypoint":"sdk-cli"
  1748 "entrypoint":"claude-desktop"
   208 "entrypoint":"sdk-ts"

터미널에서 직접 띄운 세션은 cli, Agent SDK로 만든 세션은 sdk-cli다. 아까 그 Paseo 세션을 확인해보면 예상대로다.

$ grep -ho '"entrypoint":"[^"]*"' ~/.claude/projects/*/7f3c9a21-*.jsonl | sort | uniq -c
 355 "entrypoint":"sdk-cli"

이슈 #493은 Claude Code의 /resume 목록이 entrypoint=cli만 보여주기 때문에 Paseo에서 만든 세션이 native TUI에서는 아예 안 보인다고 적고 있다. 사실이면 claude --resume <session-id>로 ID를 직접 넘기는 방법밖에 없다는 뜻이다.

다만 이 부분은 지금도 그런지 확신하지 못하겠다. 현재 내 claude 바이너리(2.1.233)에서 entrypointcli와 비교해 거르는 코드는 찾지 못했다. 이슈가 올라온 뒤 바뀌었을 수도 있다. 이건 2편에서 직접 /resume을 열어보고 확인해야 할 항목으로 남겨둔다.

정리하면 경로 자체는 막혀 있지 않다. 다만 지금은 두 군데를 손으로 이어 붙여야 한다.

모바일은 무엇에 연결되는가

세션 이야기는 여기까지고, 이제 네트워크다. 밖에 있는 핸드폰이 개발 PC의 에이전트에 어떻게 닿는가.

내 환경에는 이미 외부에서 내부망으로 들어오는 VPN이 있다. 가장 단순하고 보수적인 방법은 이걸 쓰는 것이다. 보안 구조도 갖춰져 있고 새로 인터넷에 노출할 서비스도 없다.

문제는 귀찮다는 것이다. Claude가 질문 하나 던졌는지 보려고 매번 VPN을 켜야 한다면, 폰을 꺼내서 바로 확인한다는 경험과는 거리가 있다.

Paseo는 다른 길도 준다. 데몬이 릴레이로 먼저 outbound 연결을 만들고 모바일도 같은 릴레이에 붙는다. 개발 PC에 inbound 포트를 열 필요가 없고 NAT 뒤에 있어도 상관없다. 그 위에 Paseo 자체 E2EE가 한 겹 더 있다.

공식 문서는 릴레이를 신뢰하지 않는 전송로로 취급한다. 릴레이가 보는 것은 IP, 타이밍, 메시지 크기, 세션 ID뿐이고 메시지 내용을 읽거나 위조하거나 핸드셰이크를 관찰해 키를 유도할 수는 없다고 적혀 있다. 신뢰의 기준점은 QR 코드나 페어링 링크다. 여기에 데몬의 공개키가 들어 있다. 그래서 문서도 이걸 비밀번호처럼 다루라고 한다.

공식 릴레이 대신 직접 운영할 수도 있다. 릴레이는 getpaseo/paseo-relay라는 별도 저장소에 Elixir로 나와 있다. 나는 이미 Linux 게이트웨이가 있으니, 인터넷을 마주하는 TLS 종단은 오래 검증된 nginx에 맡기고 릴레이를 그 뒤에 두는 구성을 시도해볼 생각이다. Tailscale로 붙는 경로도 문서에 따로 정리돼 있다.

처음에 잘못 이해한 것

처음에는 이렇게 정리했다. Orca는 직접 연결, Paseo는 릴레이.

이 비교는 틀렸다. Orca도 companion 앱을 페어링해서 쓰고 원격 박스에서 에이전트를 돌리는 SSH worktree 기능도 있다. 반대로 Paseo도 직접 TCP, Tailscale, VPN을 다 지원한다. 네트워크 topology만으로는 둘이 갈리지 않는다.

진짜 차이는 무엇이 본체인가에 가깝다.

Orca에서는 데스크톱이 source of truth다. 모바일은 데스크톱 터미널을 그대로 비추는 얇은 클라이언트고, 키보드 입력이 되니 사실상 뭐든 칠 수 있지만 폰에서 도는 건 뷰어뿐이다. Paseo에서는 데몬이 에이전트 control plane이고 데스크톱과 모바일이 같은 자격으로 거기에 붙는다.

그래서 “Orca는 모바일이 약하고 Paseo는 강하다”는 식의 정리도 정확하지 않다. 소유권의 위치가 다른 것이다.

진짜 문제는 권한이다

원격 에이전트 환경에서 제일 무서운 건 대화 내용 유출만이 아니다. 모바일에서 Claude에게 새 일을 시킬 수 있다는 건, 내 핸드폰이 개발 PC에서 명령을 실행할 수 있다는 뜻이다.

그 PC에는 git 저장소만 있는 게 아니다. SSH 키, 클라우드 자격증명, Docker, 각종 API 키가 있다.

그러니 릴레이가 TLS를 쓰느냐보다 중요한 질문은 이거다. 누가 이 에이전트 런타임에 명령을 보낼 수 있는가.

Paseo의 QR 페어링과 공개키 기반 E2EE는 설계상 합리적이다. 하지만 설계가 합리적인 것과, OpenSSH나 nginx처럼 수십 년간 인터넷에 노출돼 두들겨 맞은 것은 다른 문제다. 취약하다는 말이 아니라 검증의 종류가 다르다는 말이다.

그리고 정상적으로 인증된 모바일 클라이언트는 결국 꽤 강한 권한을 갖는다. 그래서 나는 릴레이를 어떻게 보호할지보다 필요할 때만 켤 수 있느냐가 더 중요하다고 생각하게 됐다.

이건 확인해보니 이미 그렇게 돼 있었다. 새로 설치하면 릴레이는 꺼져 있고 페어링할 때 켤지 물어본다. 켜지 않으면 페어링 QR도 만들어지지 않고 데몬은 직접 TCP나 Tailscale로만 접근된다. 내가 원하던 kill switch가 기본값이었던 셈이다.

모바일 연결성은 얻고 싶지만 개발 PC를 365일 원격 제어 가능한 상태로 두고 싶지는 않다.

다시 OpenCode 이야기로 돌아온다

여기까지 파고들다 결국 처음 문제로 돌아왔다. Anthropic은 이 사용 방식을 계속 허용할까?

처음에는 그냥 막연한 걱정이었다. 그런데 현재 정책 문서를 확인해보니 생각보다 구체적이었다. Anthropic은 서드파티 개발자가 자기 제품에서 claude.ai 로그인을 제공하거나, 사용자를 대신해 Free/Pro/Max 플랜 자격증명으로 요청을 라우팅하는 것을 허용하지 않는다. API 키 인증을 쓰라고 명시하고 사전 통보 없이 제한 조치를 취할 수 있다고도 적혀 있다.

즉 Anthropic 입장에서 공식 claude CLI를 쓰는 것과 서드파티 애플리케이션이 Agent SDK로 Claude를 제공하는 것은 같은 것이 아니다.

여기서 Orca와 Paseo의 구조 차이가 다시 중요해진다.

Orca는 공식 CLI를 실행한다. Claude Code가 인증하고, Anthropic과 통신하고, 자기 세션을 만든다. 이것까지 막으려면 Warp나 iTerm, tmux 같은 다른 터미널 호스트와 어디서 선을 그을지가 애매해진다. 미래 정책을 단정할 수는 없지만 OpenCode에서 문제가 됐던 구조와는 꽤 멀리 떨어져 있다.

Paseo의 Claude 어댑터는 Agent SDK 위에 있다. ACP로 옮기더라도 이 문제가 사라지지는 않는다. transport와 통합 구조는 깔끔해져도 인증과 과금 경로는 여전히 Agent SDK 정책의 영향을 받는 자리에 남는다.

다만 한 가지는 짚어야 공정하다. 정책 문구가 겨냥하는 건 서드파티가 자기 사용자들에게 Claude를 제공하는 형태다. Paseo는 내 개발 PC에서 내 자격증명으로 도는 로컬 도구다. 호스팅 서비스와는 모양이 다르다. 그래도 Agent SDK라는, 정책이 따로 적용되는 계층 위에 있다는 사실은 남는다.

그러니 질문은 “Anthropic이 Paseo를 싫어하면 어떡하지”가 아니다. 정책이 바뀌면 영향을 받을 수 있는 위치에 있는가이고, 답은 그렇다는 것이다. 곧 막힌다고 볼 근거는 없다. 리스크가 어느 계층에 있는지를 알 수 있을 뿐이다.

막히면 무엇을 잃는가

최악을 가정해보자. 어느 날 Agent SDK 경로에서 지금 같은 구독 사용이 막힌다. 그러면 Paseo에서 새 Claude 에이전트를 띄우는 흐름은 깨진다.

하지만 앞에서 확인했듯 이미 만들어진 세션은 Paseo 전용 포맷에 갇혀 있지 않다. runtimeInfo.sessionId가 있고 ~/.claude 아래에 transcript가 있다. claude --resume으로 빠져나올 수 있다.

이 차이는 크다. Paseo가 막혀도 몇 주 쌓아온 작업 컨텍스트까지 날아가지는 않는다. 잃는 건 주로 orchestration surface다. 모바일에서 새 에이전트를 만드는 것, 데스크톱과 모바일이 같은 control plane을 보는 것, workspace와 multi-agent 관리 같은 것들이다.

즉 Paseo의 리스크는 내 데이터를 인질로 잡는 lock-in 쪽이 아니라, 잘 쓰던 통합이 정책 때문에 어느 날 멈출 가능성 쪽에 가깝다.

지금 시점의 정리

Claude Code 단독OrcaPaseo
Claude 실행터미널에서 직접PTY로 공식 CLIAgent SDK query()
세션 저장 위치~/.claude~/.claude~/.claude
transcript entrypointclicli (문서 기준)sdk-cli (실측)
본체Claude Code데스크톱데몬
모바일없음데스크톱 터미널 미러데몬에 붙는 1급 클라이언트
원격 경로직접 구성페어링 · SSH worktree릴레이(E2EE) · Tailscale · 직접 TCP
Anthropic 정책 노출공식 CLI 경로공식 CLI 경로Agent SDK 경로

Orca 열은 공식 문서를 근거로 적은 것이고 아직 설치해서 재보지 않았다. Paseo 열의 sdk-cli는 내 개발 PC에서 확인한 값이다.

처음 질문은 간단했다. Claude Code를 핸드폰에서도 쓸 수 없을까. 실제 질문은 조금 달랐다. 잘 쓰고 있는 Claude Code 중심 환경을 최대한 보존하면서 모바일 연결성을 얻는 방법은 무엇인가. 그리고 정책 변경 한 번에 환경 전체가 흔들리는 구조는 피하고 싶다.

이 기준으로 보면 답이 갈린다. 모바일 연결성과 cross-device orchestration을 극대화하려면 Paseo가 매력적이다. Claude Code를 끝까지 system of record로 두고 OpenCode 때 같은 리스크를 피하고 싶다면 Orca가 자연스럽다.

그리고 중요한 게 하나 있다. 나는 데스크톱 개발환경이 불편해서 이 도구들을 찾은 게 아니다. Warp + Claude Code에 만족하고 있다. 그러니 Orca든 Paseo든 데스크톱 UI가 아무리 좋아도 그 자체는 나에게 구매 포인트가 아니다. 내가 감수하면서 환경을 바꿀 이유는 모바일 핸드오프가 그만큼 좋아지는 경우뿐이다.

다음 편

같은 개발 PC에 Orca와 Paseo를 둘 다 깔아볼 생각이다. Paseo는 기존 네트워크를 이용해 릴레이 자체 운영도 시도해본다.

그리고 이 시나리오를 반복해볼 예정이다. 개발 PC에서 Claude 작업 시작 → 외부로 이동 → 핸드폰에서 기존 세션 이어받기 → 핸드폰에서 새 작업 생성 → 복귀 → 개발 PC에서 native Claude Code로 다시 이어가기.

확인하고 싶은 건 스펙표가 아니다.

  • 개발 PC에서 만든 세션이 Paseo와 Orca 각각에서 어떻게 보이는가
  • Paseo에서 만든 세션이 /resume 목록에 실제로 뜨는가. 안 뜨면 몇 번의 조작이 필요한가
  • 이미 구성해둔 CLAUDE.md, skills, MCP가 어디까지 그대로 동작하는가
  • 두 제품의 모바일 화면이 실사용에서 얼마나 다른가

마지막 항목은 이번 편에서 개념도까지만 그렸다. 실제 UI 비교는 둘 다 깔고 같은 세션을 양쪽에서 열어봐야 의미가 있어서 2편에서 같은 작업을 두 화면으로 나란히 찍을 생각이다.

둘 다 마음에 안 들면 다른 방향도 있다. Claude Code 자체의 Agent View, cmux처럼 터미널을 중심에 두는 도구, tmux 기반 접근도 있다. 아예 tmux + Claude Code + VPN 조합이 낫다는 결론이 날 수도 있다.

결국 2편에서 확인하고 싶은 건 하나다. Claude Code를 모바일에서 이어 쓰려고 어디까지 양보할 만한가.

여기까지 파보고 하나는 확실해졌다. AI 코딩 도구를 고를 때 모델 성능이나 UI만 보면 되는 문제가 아니다. 에이전트가 늘어나고 기기를 오가기 시작하면 이런 질문이 더 중요해진다. 누가 프로세스를 실행하는가. 누가 세션을 소유하는가. 모바일은 무엇에 연결되는가. 그리고 이 도구가 내일 사라져도 나는 원래 환경으로 돌아갈 수 있는가.


참고

🏷️