SNA

세션

SnaSession과 RuntimeSession으로 나뉘는 두 계층 세션 모델.

SnaSession은 앱이 다루는 영구 세션입니다: 안정적인 id, 라벨, cwd, meta를 갖습니다. RuntimeSession은 그 세션 위에 마운트되는 구체적인 런타임 설정입니다: provider, model, permission mode, MCP 서버, hooks. SnaSession 하나는 생애 동안 여러 RuntimeSession을 가질 수 있고, runtime chain은 그 세션에 적용된 런타임 설정의 순서 있는 기록입니다.

왜 두 계층

사용자가 Claude Code로 채팅을 시작했다가 스레드 도중 Codex로 바꾸고, 그다음 reasoning 레벨을 올렸다면 SnaSession은 하나지만 RuntimeSession은 셋입니다. 각 블록의 어트리뷰션이 정확히 유지되고, replay 시 각 런타임의 네이티브 히스토리를 이전 턴 손실 없이 재구성할 수 있습니다.

일반적인 이어가기 흐름은 같은 SnaSession에 머무릅니다. 다음 턴도 agent.send로 보내면 됩니다. SNA가 활성 런타임의 대화/thread를 유지하므로 앱이 매 턴 히스토리를 다시 로드하거나 프롬프트 캐시를 설정하지 않아도 됩니다. resume은 멈춘 프로세스, 명시적 복구, 정규화 히스토리 재주입이 필요한 런타임 경계에서 쓰는 경로입니다.

라이프사이클

POST /agent/sessions      → SnaSession 생성 (프로세스 없음)
POST /agent/start         → 초기 RuntimeSession 마운트, 프로세스 spawn
POST /agent/send          → 메시지 전송, 이벤트 흐름
POST /agent/set-model     → 인플레이스 오버라이드 또는 새 RuntimeSession
PATCH /agent/session      → {cwd, model, permissionMode} 통합 mutator
POST /agent/resume        → 런타임 네이티브 히스토리 재구성, respawn
POST /agent/restart       → 같은 설정으로 respawn (그대로 유지)
POST /agent/kill          → 프로세스 종료 (세션 레코드는 남음)
DELETE /agent/sessions/:id → 세션, 히스토리, runtime chain, 대기 중인 권한 삭제

삭제는 되돌릴 수 없습니다. 실행 중인 프로세스가 있으면 먼저 종료하고, 대기 중인 권한 요청은 거부로 처리합니다. 저장된 채팅 및 런타임 레코드도 삭제되며, 이후 그 세션으로 들어오는 호출은 no-session 또는 no-active-session 오류를 반환합니다.

PATCH 의미론

PATCH /agent/session은 통합 mutator입니다. 서버는 활성 런타임 어댑터의 applyPatch() 훅을 통해 필드별로 적용하고, 그 훅이 인플레이스 적용과 respawn-with-history-replay 중 하나를 결정합니다.

필드claude-codecodexopencodegrok / cursor
model인플레이스per-turn 오버라이드인플레이스respawn + replay
permissionMode인플레이스per-turn 오버라이드인플레이스인플레이스 (SNA gate)
cwdrespawn + replay인플레이스 (per-thread cwd)respawnrespawn + replay

응답의 applied 필드가 어느 경로를 탔는지 알려주므로, 클라이언트는 "reloading…" 표시를 필요할 때만 보여줄 수 있습니다.

왜 이게 중요한가

대부분의 "에이전트 플랫폼" 추상화는 설정을 바꾸는 순간 세션을 새로 만들게 합니다. SNA는 설정을 세션 레코드의 일급 축으로 다루므로, 하위 런타임 설정이 바뀌어도 UX가 이어집니다.

목차