세션
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-code | codex | opencode | grok / cursor |
|---|---|---|---|---|
model | 인플레이스 | per-turn 오버라이드 | 인플레이스 | respawn + replay |
permissionMode | 인플레이스 | per-turn 오버라이드 | 인플레이스 | 인플레이스 (SNA gate) |
cwd | respawn + replay | 인플레이스 (per-thread cwd) | respawn | respawn + replay |
응답의 applied 필드가 어느 경로를 탔는지 알려주므로, 클라이언트는
"reloading…" 표시를 필요할 때만 보여줄 수 있습니다.
왜 이게 중요한가
대부분의 "에이전트 플랫폼" 추상화는 설정을 바꾸는 순간 세션을 새로 만들게 합니다. SNA는 설정을 세션 레코드의 일급 축으로 다루므로, 하위 런타임 설정이 바뀌어도 UX가 이어집니다.