콘텐츠로 이동

세션 및 대화 상태

게이트웨이, 채널, 크론 작업 전반에 걸쳐 세션이 범위 지정되고, 영속화되고, 직렬화되고, 공유되는 방식을 설명합니다.

세션은 단일 대화 스레드로, 순서가 있는 메시지 목록과 소량의 상태 정보(대화 상대, 현재 처리 중인 턴 여부, 마지막 활동 시각)로 구성됩니다. Revka는 세션을 유지함으로써 에이전트가 재시작 후 컨텍스트를 복원하고, 대시보드에서 이전 대화를 목록으로 보거나 다시 열 수 있으며, 예약된 작업이 사용자가 이미 읽고 있는 스레드에 결과를 추가할 수 있도록 합니다.

이 페이지에서는 세션의 범위 지정 및 이름 규칙, 영속화 방식, 동시 턴의 직렬화 처리, 그리고 세션 작업에 사용하는 REST 엔드포인트·툴·설정 키를 설명합니다. 게이트웨이 위에 채팅 UI를 구축하는 경우 실시간: WebSocket, SSE & Live Canvas크론, 세션 & 첨부파일 API도 참고하세요.

모든 대화 채널에서 세션이 생성되지만, 세션은 두 개의 별도 저장소에 보관됩니다.

  • 게이트웨이(대시보드) 세션/ws/chat WebSocket에 의해 생성됩니다. 대시보드에 표시되는 채팅이 이에 해당합니다. gw_ 접두사가 붙은 키로 저장되며, [gateway] session_persistence가 활성화된 경우 SQLite에 영속화됩니다.
  • 채널 세션 — Telegram, Discord, Slack, Matrix 등의 메시징 채널에서 메시지가 수신될 때 생성됩니다. 키에는 채널 이름이 접두사로 붙습니다(예: discord_…, telegram_…). [channels] session_persistence가 활성화된 경우 {workspace}/sessions/ 경로에 영속화됩니다.

GET /api/sessions 엔드포인트는 두 종류의 세션을 모두 반환합니다. 게이트웨이 세션은 항상 포함되며, 채널 영속화가 활성화된 경우 채널 세션도 포함됩니다.

세션은 세션 ID로 식별되지만, 영속화 레이어는 채널 접두사가 추가된 세션 키로 저장합니다. 원시 저장소를 읽거나 에이전트 간 툴을 사용할 때 이 차이를 이해하는 것이 중요합니다.

채널전달하는 세션 ID저장 키비고
게이트웨이 / 대시보드UUID(자동 생성) 또는 operator-main 같은 지정 이름gw_<id>REST API는 gw_ 접두사를 숨기므로 경로에 ID만 전달합니다.
채널 (스레드 없음)발신자에서 파생<channel>_<sender>예: telegram_alice
채널 (스레드 있음)스레드 + 발신자에서 파생<channel>_<thread>_<sender>Slack/Discord 스레드는 스레드 ID를 포함합니다.

핵심 사항:

  • /ws/chat에서 session_id를 생략하면 게이트웨이가 새 UUID를 발급하고, 기존 ID(또는 operator-main 같은 고정 이름)를 전달하면 세션을 이어서 진행합니다.
  • 범용 /webhook 엔드포인트는 선택적 X-Session-Id 헤더를 통해 실행 범위를 특정 세션으로 한정합니다.
  • 에이전트 간 세션 툴(아래 참조)은 session_id전체 세션 키를 받으며, 채널 세션의 경우 해당 툴에서 사용하는 규칙은 channel__identifier 형식입니다(예: telegram__alice).

영속화는 두 저장소에 대해 독립적으로 제어됩니다. 기본값은 모두 활성화입니다.

[gateway]
# Persist gateway WebSocket chat sessions to SQLite. Default: true.
session_persistence = true
# Auto-archive stale gateway sessions older than N hours. 0 = disabled. Default: 0.
session_ttl_hours = 0
타입기본값설명
session_persistencebooltruefalse로 설정하면 게이트웨이는 세션 백엔드를 유지하지 않습니다. 세션 목록/메시지/상태 엔드포인트는 session_persistence: false와 함께 빈 결과를 반환하며, 채팅은 재시작 후 유지되지 않습니다.
session_ttl_hoursu3200보다 크면, 데몬 시작 시 이 시간보다 오래된 세션이 아카이브됩니다. 0은 TTL 정리를 비활성화합니다.
[channels]
# Persist channel conversation history so sessions survive daemon restarts.
# Files are stored in {workspace}/sessions/. Default: true.
session_persistence = true
# Backend: "sqlite" (default) or "jsonl" (legacy). SQLite adds FTS5 search,
# metadata tracking, and TTL cleanup.
session_backend = "sqlite"
# Auto-archive stale channel sessions older than this many hours. 0 disables. Default: 0.
session_ttl_hours = 0
타입기본값설명
session_persistencebooltrue재시작 후에도 채널 대화 기록을 유지합니다.
session_backendstring"sqlite""sqlite"(FTS5 검색, 메타데이터, TTL 지원) 또는 "jsonl"(레거시 플랫 파일).
session_ttl_hoursu320N시간보다 오래된 채널 세션을 자동 아카이브합니다. 0은 비활성화합니다.

게이트웨이 세션 영속화는 대시보드 Config 페이지에서 전환할 수 있습니다. 크론, 비용 & 설정 페이지를 참고하세요.

채팅 세션 — 목록 조회, 메시지, 상태, 삭제, 이름 변경

섹션 제목: “채팅 세션 — 목록 조회, 메시지, 상태, 삭제, 이름 변경”

모든 세션 엔드포인트는 페어링 Bearer 토큰을 필요로 합니다. 토큰 발급 방법은 페어링 & 인증을 참고하세요.

Authorization: Bearer <token>
GET /api/sessions

최근 활동 순으로 정렬된 모든 세션(게이트웨이 + 채널)과 아카이브된 세션 ID 목록을 반환합니다.

{
"sessions": [
{
"id": "8d43b6ef-0f18-4c3f-b04c-3a03f79e2c72",
"channel": "dashboard",
"started_at": "2026-06-18T09:00:00+00:00",
"last_activity": "2026-06-18T09:14:22+00:00",
"status": "active",
"message_count": 12,
"name": "Release planning"
},
{
"id": "telegram_alice",
"channel": "telegram",
"started_at": "2026-06-17T20:10:00+00:00",
"last_activity": "2026-06-17T20:31:05+00:00",
"status": "idle",
"message_count": 4
}
],
"archived_session_ids": ["3f0c…"]
}

status 필드는 대략적인 최근성 플래그입니다. 마지막 활동이 5분 이내이면 active, 그 외에는 idle입니다.

GET /api/sessions/running

현재 턴이 처리 중인(running) 세션만 반환합니다. 각 항목에는 session_id, created_at, last_activity, message_count가 포함됩니다.

GET /api/sessions/{id}/messages
{
"session_id": "8d43b6ef-…",
"messages": [
{ "role": "user", "content": "Plan the next release" },
{ "role": "assistant", "content": "Here is a draft plan…" }
],
"session_persistence": true
}
GET /api/sessions/{id}/state
{
"session_id": "8d43b6ef-…",
"state": "running",
"turn_id": "f1c2…",
"turn_started_at": "2026-06-18T09:14:00+00:00"
}

stateidle, running, error 중 하나입니다. turn_idturn_started_at은 턴이 활성 상태일 때만 나타납니다. 세션이 없으면 404를 반환합니다.

PUT /api/sessions/{id}
Content-Type: application/json
{ "name": "Release planning" }

이름은 비어 있으면 안 됩니다. 세션이 이미 존재해야 하며, 없으면 404를 반환합니다. 응답: {"session_id": "…", "name": "Release planning"}.

DELETE /api/sessions/{id}

삭제 시 세션을 완전히 지우지 않고 아카이브(소프트 삭제)합니다. 기록은 복구 가능한 상태로 남으며, ID는 목록 응답의 archived_session_ids에 나타납니다.

{ "deleted": true, "archived": true, "archived_at": "2026-06-18T10:00:00+00:00", "session_id": "8d43b6ef-…" }

파일은 특정 세션에 첨부되며, 이후 WebSocket 메시지에서 ID로 참조됩니다.

POST /api/sessions/{session_id}/attachments
Content-Type: multipart/form-data

응답에는 file_id가 포함되며, 다음 /ws/chat 메시지의 attachments 배열에 이를 포함하면 됩니다. 이미지는 비전 모델용 [IMAGE:…] 마커로, 그 외 파일은 [DOCUMENT:…]로 변환됩니다. 파일당 최대 25 MiB입니다. 전체 첨부파일 흐름은 크론, 세션 & 첨부파일 API를 참고하세요.

하나의 세션에서 두 개의 턴이 동시에 실행되면 SQLite 트랜스크립트 쓰기가 손상되고 상태 전이가 뒤섞일 수 있습니다. 게이트웨이는 세션별 액터 큐로 이를 방지합니다. 각 세션은 최대 하나의 처리 중인 턴만 허용하며, 추가 요청은 FIFO 순서로 대기합니다.

/ws/chat에서의 동작:

  • 세션당 하나의 턴. 처리 중인 세션에 두 번째 메시지가 도착하면 현재 턴이 완료될 때까지 대기합니다.
  • 대기열 제한. 대기열 깊이 한도에 도달하면 새 요청은 SESSION_BUSY WebSocket 오류 코드와 함께 거부됩니다. 오류 메시지는 Session <id> queue full (<N> pending requests)입니다.
  • 락 타임아웃. 락 타임아웃보다 오래 대기한 요청은 Previous message is still being processed — please wait, or retry once it completes (timeout: <N>s) 오류와 함께 실패합니다. 이 메시지에는 세션 ID가 의도적으로 포함되지 않습니다.
  • 유휴 제거. 유휴 TTL을 초과한 세션 슬롯은 메모리에서 제거됩니다(영속화된 트랜스크립트는 영향을 받지 않습니다).

락 타임아웃의 기본값은 300초이며, 환경 변수로 설정합니다.

환경 변수기본값설명
REVKA_GATEWAY_SESSION_LOCK_TIMEOUT_SECS300큐에 대기 중인 턴이 세션 락을 획득하지 못할 경우 타임아웃까지 대기하는 시간(초). 유효하지 않거나 0인 값은 경고를 기록하고 기본값으로 폴백합니다.

세 가지 에이전트 툴을 통해 한 에이전트가 다른 대화의 세션을 읽고 쓸 수 있습니다. 이는 에이전트 간 통신의 기반이 됩니다. 이 툴들은 전체 세션 키를 사용하여 세션 백엔드에 직접 접근합니다.

채널, 메시지 수, 마지막 활동 시각과 함께 활성 세션 목록을 반환합니다.

  • limit (integer, 선택, 기본값 50) — 반환할 최대 세션 수.

각 줄에 하나의 세션을 나타내는 텍스트 형식으로 반환합니다: - <key>: channel=<channel>, messages=<N>, last_activity=<ts>.

특정 세션의 최근 메시지 기록을 읽습니다.

  • session_id (string, 필수) — 세션 키, 예: telegram__user123.
  • limit (integer, 선택, 기본값 20) — 반환할 최근 메시지 수.

보안 정책에서 Read 툴 권한이 필요합니다. 비어 있거나 알 수 없는 세션은 오류 대신 “No messages found” 안내를 반환합니다.

대상 세션의 기록에 user 메시지를 추가하여 대상 에이전트가 다음 턴에 메시지를 받을 수 있도록 합니다.

  • session_id (string, 필수) — 대상 세션 키.
  • message (string, 필수) — 전송할 내용. 비어 있으면 안 됩니다.

Act 툴 권한이 필요합니다. ReadAct 권한 게이팅의 동작 방식은 툴 개요보안 모델을 참고하세요.

에이전트 크론 작업은 session_target으로 실행할 세션을 지정합니다.

동작
isolated (기본값)각 실행은 빈 컨텍스트에서 새로 시작합니다. 대부분의 자동화 작업에 권장됩니다.
main작업이 사용자의 기본 인터랙티브 채팅과 동일한 세션에서 실행되어 최근 대화 컨텍스트를 참조할 수 있습니다.
{
"schedule": { "kind": "cron", "expr": "0 9 * * 1-5", "tz": "America/New_York" },
"job_type": "agent",
"prompt": "Summarize overnight alerts",
"session_target": "isolated",
"delivery": { "mode": "announce", "channel": "discord", "to": "1234567890" }
}

session_target은 에이전트 작업을 생성하는 모든 곳에서 사용할 수 있습니다. cron_add 툴, [[cron.jobs]] 설정 블록, POST /api/cron 본문 모두에서 지원됩니다.

전체 스케줄링 기능은 revka cron에이전트 작업 & 전달 아래의 크론 가이드를 참고하세요.

  1. 클라이언트가 기존 session_id/ws/chat에 연결하거나, 생략하여 새 UUID를 발급받습니다. 게이트웨이는 resumedmessage_count를 포함한 session_start를 전송합니다.

  2. 클라이언트가 message를 전송합니다. 액터 큐가 세션 락을 획득하고 세션 상태가 turn_id와 함께 running으로 전환됩니다.

  3. 에이전트는 chunk 프레임, tool_call / tool_result 이벤트, 마지막으로 done 프레임을 스트리밍합니다. 상태는 idle로 돌아옵니다.

  4. 락을 획득하지 못하면 클라이언트는 SESSION_BUSY 오류를 받습니다. 턴 중간에 실패가 발생하면 상태는 error가 됩니다.

  5. 영속화가 활성화된 경우, 트랜스크립트가 SQLite에 기록되어 재시작 후 GET /api/sessions/{id}/messages로 조회할 수 있습니다.