멀티플렉서

tmux, zellij, 그리고 둘 다 없을 때.

--mux 가 백엔드를 고릅니다. 그냥 두면 tmux를 먼저 찾고 없으면 바이너리 안의 zellij로 갑니다. Windows에서는 st가 담아 오는 Windows용 tmux, psmux가 tmux 자리를 대신합니다.

st --mux zellij                    # tmux가 있어도 동봉된 zellij로
st --mux none --command python3    # 멀티플렉서 없이 — 세션 유지도 없습니다
tmuxzellijnone
연결이 끊겨도 살아남는다예예아니오
클라이언트마다 자기 현재 창예아니오해당 없음
키바에서 윈도우 목록예예해당 없음
현재 윈도우 표시예아니오해당 없음
워커(오케스트레이션)예아니오아니오

psmux는 tmux처럼 연결이 끊겨도 세션을 유지하지만, 세션마다 서버를 하나씩 띄우고 세션 그룹이 없어서 한 세션의 클라이언트들이 그 세션의 현재 윈도우를 같이 씁니다. psmux 3.4.0과 st 0.3.97부터는 psmux에서도 워커가 실행됩니다. 실행 중인 에이전트를 데려오는 방식이고, st가 Windows에서 새 워커를 띄우지는 않습니다.

멀티플렉서가 여기 있는 이유는 지속성 하나입니다. ttyd는 웹소켓이 닫히는 순간 자식 프로세스에 SIGHUP을 보내고 다시 붙으면 새 pty를 줍니다. --mux none 으로는 화면 잠금, 앱 전환, 망 변경 하나하나가 세션의 끝입니다.

per-client 뷰

브라우저는 사용자의 세션에 직접 붙지 않습니다. 각자 같은 세션 그룹 안의 자기 세션을 받습니다:

tmux new-session -A -s sophonz__a1b2c3d4 -t sophonz

그룹 멤버는 윈도우 목록을 공유하면서 현재 창은 각자 갖습니다. aggressive-resize 를 켜면 창은 그 창을 현재 창으로 보고 있는 클라이언트에 맞춰지므로, 서로 다른 창을 보는 두 클라이언트는 각자의 크기를 갖습니다.

같은 창을 보는 두 클라이언트는 그럴 수 없습니다. 세션 그룹에서 창 크기는 모두에게 하나입니다. 기본값은 latest 라, 가장 최근에 움직인 쪽이 크기를 정하고 모두가 온전한 줄을 봅니다.

그래도 창이 브라우저보다 넓으면, 페이지가 줄이 들어갈 때까지 글꼴을 줄입니다. 읽을 수 없는 것을 그리느니 8px에서 멈춥니다.

zellij에는 세션 그룹에 해당하는 것이 없어 두 번째 클라이언트가 같은 세션과 크기를 공유합니다. 시작 화면이 그 사실을 알려줍니다.

tmux 서버가 죽었을 때

tmux 서버가 죽으면 그 안의 셸과 프로그램도 함께 종료됩니다. st는 10초마다 서버의 스냅샷(세션, 창, 레이아웃, 각 패널의 디렉터리와 실행 중이던 프로그램)을 기록하고, 마지막으로 확인한 서버가 아닌 서버를 발견하면 이를 다시 구성합니다.

  • 세션은 이름, 창, 레이아웃, 디렉터리가 그대로 복원됩니다. 이미 있는 세션은 변경하지 않습니다.
  • 에이전트는 대화를 이어 갑니다. Claude Code나 Codex 패널에는 이어가기 명령(claude --resume <id>, codex resume <id>)이 입력되고, st가 어느 대화였는지 알고 그 대화가 이 장치에 저장되어 있으면 Enter까지 직접 누릅니다(st 0.3.94 이상). 그렇지 않으면 명령만 입력해 두고, 그 이유를 패널에 표시합니다. 대화 id는 에이전트 훅에서, 코디네이터와 워커는 실행 기록에서도 가져옵니다(st 0.3.95 이상).
  • 그 밖의 명령은 다시 실행하지 않습니다. 각 패널은 실행 중이던 프로그램을 표시한 뒤 셸을 시작합니다. 빌드, 배포, 마이그레이션은 사용자가 직접 실행합니다.
  • 다음 st가 "tmux stopped at 14:02 — st restored 3 sessions"와 함께 이어 간 에이전트 수를 알려 주고, 폰에도 알림이 전송됩니다.
  • 브라우저와 앱에서는 복원된 창에 창 목록의 restored 표시가 붙고, 그 창에서 무언가가 실행되면 사라집니다. st가 명령만 입력해 둔 에이전트 창은 그 사실도 함께 표시합니다.

컴퓨터를 재시작한 뒤에는 st가 스스로 복원하지 않습니다(st 0.3.95 이상). 마지막 스냅샷은 사용자가 결정할 때까지 대기합니다. 데스크톱 앱이 복원 여부를 묻고, st restore --pending으로도 확인할 수 있습니다. 그동안 st는 자체 세션을 만들지 않으므로 스냅샷이 밀려나지 않습니다. 거절해도 스냅샷은 남으며, st restore --from <id>로 나중에 복원할 수 있습니다.

st restore --pending     # 재시작 뒤 대기 중인 스냅샷
st restore               # 그 스냅샷 복원
st restore --decline     # 복원하지 않음
st restore --list        # 보관된 스냅샷(다섯 개)
st restore --dry-run     # 복원하면 패널마다 무엇을 할지
st restore --from <id>   # 하나를 선택해 직접 복원
st restore --no-resume-agents   # 에이전트 명령을 입력만 하고 Enter는 누르지 않음

~/.sophonz/restore.json으로 기본값을 변경합니다.

{ "auto": true, "resumeAgents": true, "scrollbackLines": 0 }

"resumeAgents": false로 설정하면 에이전트 명령을 입력만 합니다. scrollbackLines는 패널마다 마지막 줄들을 이 장치에만 보관했다가 복원한 패널에 표시합니다. tmux kill-server도 크래시와 구별되지 않으므로, 의도적으로 종료한 tmux가 복원되지 않기를 원하면 "auto": false로 설정합니다. 앱이나 브라우저에서 창이나 세션을 닫는 것은 크래시가 아닙니다. st가 즉시 기록하며, 마지막 세션을 닫아도 다시 복원하지 않습니다.

복원은 환경을 다시 구성하는 것입니다. 에이전트의 대화는 마지막으로 저장된 지점부터 이어지지만, 그 밖의 작업은 이어지지 않습니다. 빌드는 다시 실행해야 하고, 저장하지 않은 편집 내용은 남지 않으며, 에이전트 명령 외의 명령은 사용자가 직접 실행합니다. zellij는 세션을 스스로 복원하므로(zellij attach <이름>) st가 관여하지 않습니다.