---
read_when:
    - 사람이나 다른 에이전트가 모르는 사이에 세션을 변경하면 에이전트가 이를 감지하도록 하려는 경우
    - 상태 변경 알림, 감시 커서 또는 session_status의 changesSince를 디버깅하고 있습니다.
    - 상위 에이전트가 하위 세션과 동기화된 상태를 유지하는 방식을 이해하려고 합니다
sidebarTitle: Session state awareness
summary: '지속 가능한 세션 상태 신호 로그: 상태 버전, 감시자, 오래된 상태 알림 및 조정'
title: 세션 상태 인식
x-i18n:
    generated_at: "2026-07-12T21:34:31Z"
    model: gpt-5.6
    postprocess_version: locale-links-v1
    prompt_version: 15
    provider: openai
    source_hash: 06ec310fc482ce658eb37628ac33c4224349846d1ffd6e8edeac01bc84e56341
    source_path: concepts/session-state.md
    workflow: 16
---

여러 세션이 동일한 문제를 함께 처리할 때(관리자가 하위 에이전트에게 위임하거나, 사람이 작업자 세션에 직접 개입하거나, 두 에이전트가 [`sessions_send`](/ko/concepts/session-tool)를 통해 조율하는 경우) 각 세션은 다른 세션에 대한 가정을 형성합니다. 다른 행위자가 개입하는 순간 이러한 가정은 최신 상태가 아니게 됩니다. 세션 상태 인식은 이러한 개입을 감지하고, 영향을 받는 세션에 한 번 알리며, 해당 세션이 작업하기 전에 적은 비용으로 변경 사항을 파악할 수 있게 하는 메커니즘입니다.

다음 세 가지 요소가 함께 작동합니다.

1. **내구성 있는 신호 로그**는 세션별로 선별된 상태 변경을 기록합니다.
2. **감시자**는 대상별 커서를 유지하고 병합된 오래된 상태 알림을 한 번 받습니다.
3. **조정**은 `changesSince`를 지정한 `session_status`를 통해 정확한 변경분을 가져옵니다.

## 신호 로그

감시 중인 세션이 실질적으로 변경되면 OpenClaw는 공유 상태 데이터베이스(`session_state_events`)에 형식이 지정된 이벤트를 추가합니다. 이벤트에는 메타데이터와 한 줄 요약이 포함되며, 메시지 내용은 절대 포함되지 않습니다.

| 종류                   | 기록 시점                                                | 감시자에게 알림 |
| ---------------------- | -------------------------------------------------------- | --------------- |
| `human_direct_message` | 사람이 감시 중인 세션에 직접 턴을 전송할 때              | 예              |
| `goal_changed`         | 세션의 목표 상태가 생성, 업데이트 또는 삭제될 때         | 예              |
| `child_spawned`        | 하위 에이전트 또는 ACP 하위 세션이 생성될 때             | 아니요(커서 초기화) |
| `run_completed`        | 하위 실행이 성공적으로 종료될 때                         | 아니요(로그만 기록) |
| `run_failed`           | 하위 실행이 실패하거나 시간 초과 또는 취소될 때          | 아니요(로그만 기록) |
| `compacted`            | 세션 기록이 Compaction될 때                              | 아니요(로그만 기록) |

각 이벤트에는 행위자(`human`, `agent` 또는 `system`)가 명시됩니다. 취소되거나 시간 초과된 하위 실행은 실패로 기록되며, 정확한 결과(`cancelled`, `timeout` 또는 `error`)가 이벤트 페이로드에 보존됩니다.

세션의 **상태 버전**은 로그에서 가장 높은 시퀀스 번호이며, 정리 후에도 유지되는 내구성 있는 세션별 헤드에서 추적됩니다. 변경 사항이 기록된 세션의 `sessions_list` 행에는 `stateVersion`이 포함되며, `session_status`는 항상 이를 보고합니다.

로그 전용 종류는 알림이 아니라 조정 기록을 위해 존재합니다. 일반적인 하위 실행 완료 전달은 계속 [하위 에이전트 알림](/ko/tools/subagents)이 담당하며, 신호 로그는 이를 절대 중복하지 않습니다.

## 감시자

감시자는 대상에 대한 커서(`session_watch_cursors`)를 보유한 세션입니다. 커서는 다음 두 경로로 생성됩니다.

- **암시적(생성 연결).** 세션이 하위 에이전트 또는 ACP 하위 세션을 생성하면 상위 세션의 커서가 하위 세션의 생성 버전으로 자동 초기화됩니다. 상위 세션은 수동으로 구독할 필요가 없습니다.
- **명시적(`sessions_send watch: true`).** 모든 조정자는 생성 관계가 없는 대상을 감시할 수 있습니다. `sessions_send`에 `watch: true`를 전달하면 전송이 성공적으로 디스패치된 후 발신자가 실제로 메시지를 받은 세션의 감시자로 등록됩니다. 등록은 대상의 현재 상태 버전에서 시작하므로 이전 기록은 알림을 생성하지 않습니다. 매개변수가 설정된 경우 도구 결과에 `watched: true|false`가 표시됩니다.

감시자의 ID는 에이전트로 한정된 세션 키여야 합니다. `session.scope="global"`에서는 공유 `global` 키가 여러 에이전트에 걸쳐 모호하므로, 이러한 세션에는 내구성 있는 로그와 `changesSince`가 제공되지만 선제적 알림은 제공되지 않습니다.

감시는 자동으로 정리됩니다. 커서 행은 신호 로그의 보존 기간에 맞춰 만료되고, 감시자 세션이 재설정되면 제거되며, 어느 한쪽 세션이 삭제되면 함께 삭제됩니다. v1에는 감시 해제 동작이 없습니다.

## 알림: 여러 번이 아닌 한 번만

알림 대상 이벤트가 발생하고 감시자의 커서가 뒤처져 있으면, 감시자는 다음 턴에 시스템 알림을 한 번 받습니다.

```
세션 "agent:main:subagent:child"이(가) 변경되었습니다(다른 행위자). 작업하기 전에 조정하십시오: session_status sessionKey "agent:main:subagent:child" changesSince 12.
```

기본 세션 감시자는 Heartbeat 깨우기를 통해 즉시 활성화되며, 중첩된 하위 에이전트 감시자는 다음 턴에 알림을 받습니다.

이 프로토콜은 의도적으로 과도한 알림을 방지하도록 설계되었습니다.

- **감시자/대상 쌍당 대기 중인 알림 하나.** 대기 중에는 알림 텍스트가 바이트 단위로 동일하게 유지되고 시스템 이벤트 대기열이 이를 중복 제거하므로, 같은 대상에 빠르게 20개의 변경이 발생하더라도 감시자의 프롬프트에는 한 줄만 생성됩니다.
- **고정된 워터마크.** 알림이 대기열에 추가되면 커서가 알림 위치를 고정합니다. 이후 발생하는 실질적 이벤트는 실질적 워터마크만 진행시키며 다시 알리지 않습니다.
- **소진 시 확인하며, 중간에 작업이 발생한 경우에만 다시 엽니다.** 감시자의 턴이 알림을 소비하면 커서가 진행됩니다. 대기열 추가와 소비 사이에 추가적인 실질적 이벤트가 도착했다면 나머지에 대해 새로운 알림이 정확히 하나 열립니다.
- **자체 억제.** 감시자는 자신이 발생시킨 이벤트에 대한 알림을 받지 않습니다.
- **재시작 복구.** 대기 중인 알림은 메모리 내 대기열에 저장됩니다. Gateway가 재시작되면 시작 시 검사가 내구성 있는 커서에서 알림을 다시 구체화합니다.

## 조정

알림은 감시자가 수행해야 할 작업을 정확히 알려 줍니다. `changesSince: <version>`을 지정한 `session_status`는 커서를 진행시키지 않고 해당 버전 이후의 형식이 지정된 이벤트를 최대 200개까지 반환합니다.

```json
{
  "stateVersion": 19,
  "stateChanges": {
    "events": [
      {
        "sequence": 14,
        "kind": "human_direct_message",
        "actorType": "human",
        "summary": "Telegram을 통한 사람의 메시지"
      },
      { "sequence": 19, "kind": "goal_changed", "actorType": "human", "summary": "목표 업데이트됨" }
    ],
    "historyGap": false
  }
}
```

`historyGap: true`는 요청한 버전이 보존된 기록보다 오래되었음을 의미합니다. 응답을 정확한 변경분으로 취급하지 말고 전체 세션 상태(`sessions_history`, `session_status`)를 새로 고치십시오. 간격 신호는 정확합니다. 시퀀스 산술로 추론하지 않고 세션별 정리 워터마크에서 가져옵니다.

## 저장소 및 제한

기록은 공유 상태 데이터베이스에 저장되며, 30일 및 50,000행으로 제한됩니다. 세션별 헤드는 정리 후에도 단조 증가를 유지합니다. 기록은 최선형으로 수행됩니다. 추가 작업이 실패하면 로그에 남지만 원래 턴을 실패시키지는 않습니다. 따라서 `stateVersion`은 트랜잭션 변경 데이터 캡처 버전이 아니라 신호 로그의 헤드입니다.

현재 제한 사항은 다음과 같습니다.

- 알림 전달은 하나의 Gateway 프로세스가 공유 상태 데이터베이스를 소유한다고 가정합니다. 여러 Gateway가 내구성 있는 로그와 `changesSince`를 공유할 수 있지만, v1은 프로세스 간에 알림을 푸시하지 않습니다.
- Compaction 이벤트는 내장 런타임의 Compaction 소유자를 포함하지만, 네이티브 하니스 전용 Compaction은 완전히 기록되지 않습니다.
- 취소 결과의 상세 페이로드는 현재 ACP 하위 실행에서 생성됩니다. 네이티브 하위 에이전트의 취소는 일반적인 실패로 표시됩니다.

## 관련 문서

- [세션 도구](/ko/concepts/session-tool) — `sessions_send`, `session_status`, `sessions_list`
- [하위 에이전트](/ko/tools/subagents) — 생성 연결 및 완료 알림
- [Heartbeat](/ko/gateway/heartbeat) — 대기 중인 알림이 기본 세션을 깨우는 방식
- [세션 관리](/ko/concepts/session) — 세션 키, 범위, 수명 주기
