변경 이력
RAWP-DPS 1.0.1
1.0.0 대비 변경 사항을 정리한 문서입니다.
수정됨
Document Overview
추가된 줄 6 삭제된 줄 7
1 1
# RAWP-DPS 1.0: Data Plane Streaming Specification 2 2
3 3
**Remote Agent Wire Protocol — Data Plane Streaming** 4 4
5
| 항목 | 값 | 6
| ---- | ---------------------------------- | 7
| 상태 | Draft | 8
| 버전 | 1.0 | 9
| 날짜 | 2026-03-08 | 10
| 호환 | RAWP 1.0 (제어 평면) | 11
| 대체 | RAWP 1.0 §6 (이하 RAWP-1.0-Legacy) | 5
| 항목 | 값 | 6
| ---- | ---------------------- | 7
| 상태 | Draft | 8
| 버전 | 1.0.1 | 9
| 날짜 | 2026-03-16 | 10
| 호환 | RAWP 1.0.2 (제어 평면) | 12 11
13 12
--- 수정됨
1. 개요 (Introduction)
추가된 줄 13 삭제된 줄 16
1 1
## 1. 개요 (Introduction) 2 2
3
RAWP-DPS 1.0은 RAWP 1.0의 데이터 평면(§6)을 대체하는 독립 스트리밍 규격이다. 기존 RAWP 1.0 §6(이하 **RAWP-1.0-Legacy**)은 단순 텍스트 스트림, 승인 상호작용, 사용량 지표, 에러 보고, 파일 전송만을 정의하였으며, 에이전트 코딩 도구(Claude Code 등)가 생성하는 도구 호출 결과, 계획 문서, 태스크 관리, 컨텍스트 압축, 서브에이전트 위임 등 다양한 출력 형태를 포괄하지 못한다. 3
RAWP-DPS 1.0은 RAWP 1.0의 데이터 평면(§6)을 대체하는 독립 스트리밍 규격이다. 4 4
5 5
RAWP-DPS 1.0은 다음 목표를 달성하기 위해 설계되었다: 6 6
7 7
1. **완전성(Completeness)**: Claude Code를 포함한 에이전트 코딩 도구의 모든 출력 형태를 단일 프레임 규격으로 포맷화한다. 8 8
2. **범용성(Generality)**: 특정 에이전트에 종속되지 않으며, 임의의 에이전트 도구 체계를 수용할 수 있는 확장 가능한 타입 시스템을 제공한다. 9
3. **호환성(Compatibility)**: 알 수 없는 타입의 정방향 호환성(Forward Compatibility)과, RAWP-1.0-Legacy 대비 하위 호환성(Backward Compatibility)을 구조적으로 보장한다. 9
3. **호환성(Compatibility)**: 알 수 없는 타입의 정방향 호환성(Forward Compatibility)을 구조적으로 보장한다. 10 10
11 11
### 1.1. 요구사항 표기 규약 12 12
13 13
본 문서의 "MUST", "MUST NOT", "REQUIRED", "SHOULD", "SHOULD NOT", "OPTIONAL"은 RFC 2119를 따른다. 14 14
15 15
### 1.2. RAWP 1.0과의 관계 16 16
17 17
RAWP-DPS 1.0은 RAWP 1.0의 제어 평면(§1–§5, §7–§8)을 변경하지 않는다. 세션 초기화(§5.1), WebSocket 수립(§5.3), 보안 규격(§7)은 그대로 유지되며, 본 규격은 WSS 연결 수립 **이후** 교환되는 데이터 프레임의 포맷만을 재정의한다. 18 18
19
### 1.3. RAWP-1.0-Legacy 호환 모드 20
21
세션 초기화(§5.1) 시 `Sec-WebSocket-Protocol` 헤더로 프로토콜 버전을 협상한다: 22
23
- `rawp-dps-1.0`: 본 규격 적용 24
- `rawp-1.0`: RAWP-1.0-Legacy 적용 (기존 구현 호환) 19
### 1.3. RAWP-1.0-Legacy (지원 종료) 25 20
26
서버와 클라이언트 모두 `rawp-dps-1.0`을 우선 제안해야 하며 (SHOULD), 상대가 미지원 시 `rawp-1.0`으로 폴백해야 한다 (MUST). 21
RAWP-1.0-Legacy는 지원이 종료되었다. 모든 구현은 본 규격(`rawp-dps-1.0`)만을 사용해야 한다 (MUST). 27 22
28 23
### 1.4. 용어 정의 29 24
30 25
본 문서에서 추가로 사용하는 용어: 31 26
32
| 용어 | 정의 | 33
| --------------- | ------------------------------------------------------------------------ | 34
| Frame | WSS를 통해 송수신되는 하나의 JSON 메시지 단위 | 35
| Content Block | 단일 Frame 내에서 독립적 의미를 가지는 출력 조각 | 36
| Turn | 에이전트의 단일 추론-행동 사이클 (프롬프트 수신 → 도구 사용 → 응답 완료) | 37
| Tool Invocation | 에이전트가 외부 도구를 호출하는 행위와 그 결과의 쌍 | 38
| Namespace | 이벤트 타입의 계층적 분류 체계 (`agent.`, `tool.`, `session.` 등) | 27
| 용어 | 정의 | 28
| --------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | 29
| Agent Session | RAWP 1.0.2 §1.2에서 정의. 에이전트의 논리적 생명주기 단위. 본 규격의 모든 Frame은 `session_id` 필드를 통해 특정 Agent Session에 귀속된다. 본 문서에서 "세션"은 별도 명시가 없는 한 Agent Session을 의미한다. | 30
| WSS Connection | RAWP 1.0.2 §1.2에서 정의. Agent Session에 바인딩된 WebSocket 전송 채널. 본 규격의 Frame은 WSS Connection을 통해 송수신된다. 하나의 Agent Session에 복수의 WSS Connection이 동시에 존재할 수 있다. | 31
| Frame | WSS Connection을 통해 송수신되는 하나의 JSON 메시지 단위 | 32
| Content Block | 단일 Frame 내에서 독립적 의미를 가지는 출력 조각 | 33
| Turn | 에이전트의 단일 추론-행동 사이클 (프롬프트 수신 → 도구 사용 → 응답 완료). 하나의 Agent Session 내에서 순차적으로 발생한다. | 34
| Tool Invocation | 에이전트가 외부 도구를 호출하는 행위와 그 결과의 쌍 | 35
| Namespace | 이벤트 타입의 계층적 분류 체계 (`agent.`, `tool.`, `session.` 등) | 39 36
40 37
--- 수정됨
2. Envelope 구조 (Frame Envelope)
추가된 줄 13 삭제된 줄 1
1 1
## 2. Envelope 구조 (Frame Envelope) 2 2
3 3
### 2.1. 공통 Envelope 4 4
5 5
송수신되는 모든 WSS 프레임은 다음 JSON 구조를 준수해야 한다 (MUST). 6 6
7 7
```json 8 8
{ 9 9
"v": 1, 10 10
"type": "String (필수, 네임스페이스 기반 이벤트 타입)", 11 11
"message_id": "String (필수, UUID v4)", 12 12
"timestamp": "String (필수, ISO 8601, 밀리초 정밀도 권장)", 13 13
"session_id": "String (필수, 소속 세션 식별자)", 14 14
"turn_id": "String (선택, 현재 Turn 식별자, UUID v4)", 15 15
"parent_id": "String (선택, 이 메시지가 응답하는 대상 message_id)", 16 16
"metadata": {}, 17 17
"payload": {} 18 18
} 19 19
``` 20 20
21 21
### 필드 상세 22 22
23 23
| 필드 | 필수 | 설명 | 24 24
| ------------ | -------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------- | 25 25
| `v` | MUST | Envelope 버전. 본 규격에서는 항상 정수 `1`. 수신자는 자신이 지원하지 않는 `v` 값을 수신하면 해당 프레임을 무시하고 `session.error`로 응답해야 한다 (MUST). | 26 26
| `type` | MUST | 네임스페이스 기반 이벤트 타입. §3에서 정의. | 27 27
| `message_id` | MUST | 프레임 고유 식별자. 중복 수신 시 멱등성(Idempotency) 판단 기준. | 28 28
| `timestamp` | MUST | 발신 시각. ISO 8601. 밀리초 정밀도 권장 (SHOULD). | 29
| `session_id` | MUST | RAWP 1.0 §5.1에서 발급된 세션 식별자. | 29
| `session_id` | MUST | Agent Session 식별자 (UUID v4). RAWP 1.0.2 §5.1에서 발급. WSS Connection이 아닌 Agent Session을 식별한다. | 30 30
| `turn_id` | SHOULD | 현재 에이전트 Turn의 식별자. 동일 Turn 내 모든 프레임은 같은 `turn_id`를 공유. | 31 31
| `parent_id` | OPTIONAL | 요청-응답 상관 관계. `agent.prompt.request`에 대한 `agent.prompt.delta`는 해당 요청의 `message_id`를 `parent_id`로 참조. | 32 32
| `metadata` | OPTIONAL | 확장 및 디버깅용 자유 형식 객체. 빈 객체 `{}` 허용. | 33 33
| `payload` | MUST | 이벤트 타입별 페이로드. §3에서 각 타입별로 정의. | 34 34
35 35
### 2.2. 파싱 규약 36 36
37 37
- **Postel's Law**: 수신자는 `payload` 내 알 수 없는 키를 무시해야 한다 (MUST). 38 38
- **Unknown Type**: 수신자는 자신이 처리할 수 없는 `type`을 수신하면 해당 프레임을 무시하고, 로그에 기록해야 한다 (MUST). 에러를 발생시키거나 연결을 종료해서는 안 된다 (MUST NOT). 39 39
- **Envelope 버전**: `v` 필드가 지원 범위를 벗어나면 프레임을 무시하고 `session.error`를 발송해야 한다 (MUST). 40 40
41 41
### 2.3. 프레임 순서 보장 42 42
43 43
WSS는 메시지 순서를 보장하므로, 동일 연결 내 프레임은 발신 순서대로 수신됨을 전제한다. 단, 재연결(reattach) 후 `session.history` 프레임을 통해 과거 이력이 재전송될 때는 `timestamp`와 `message_id` 기준으로 순서를 재구성해야 한다 (MUST). 44 44
45
### 2.4. 다중 마스터 환경 (Multi-Master Fan-out) 46
47
클라이언트가 복수의 마스터 서버에 동시 등록된 환경(RAWP 1.0.2 §3 참조)에서의 DPS 프레임 분배 규칙을 정의한다. 48
49
**브로드캐스트 원칙**: 클라이언트는 하나의 Agent Session에서 발생하는 모든 DPS 프레임을 해당 세션에 연결된 **모든** WSS Connection으로 브로드캐스트해야 한다 (MUST). 여기에는 세션을 생성한 마스터의 WSS뿐 아니라, 다른 마스터가 동일 세션에 연결한 WSS도 포함된다. 50
51
- 에이전트 출력 프레임(`agent.*`, `tool.*`, `session.*`)은 해당 세션에 바인딩된 모든 WSS Connection에 전송해야 한다 (MUST). 52
- 마스터가 발송한 `control.*` 프레임(예: `control.prompt.request`)도 해당 세션에 바인딩된 다른 모든 WSS Connection에 중계해야 한다 (MUST). 이를 통해 마스터 A가 전송한 프롬프트를 마스터 B도 수신하여 사용자에게 표시할 수 있다. 53
- 로컬 UI WSS Connection(RAWP 1.0.2 §10.6)도 브로드캐스트 대상에 포함된다 (MUST). 54
- 로컬 세션(RAWP 1.0.2 §10, `origin: "local"`)의 DPS 프레임은 마스터에 전송하지 않는다 (MUST NOT). 로컬 UI WSS에만 전달한다. 55
- `control.*` 프레임의 중계 시 `message_id` 기반 멱등성 처리를 수행하여 발신자에게 자신의 프레임이 되돌아오는 것을 방지해야 한다 (MUST). 즉, 프레임을 발송한 WSS Connection에는 해당 프레임을 재전송하지 않는다. 56
45 57
--- 수정됨
3. 이벤트 타입 시스템 (Event Type System)
추가된 줄 3 삭제된 줄 18
1 1
## 3. 이벤트 타입 시스템 (Event Type System) 2 2
3 3
### 3.1. 네임스페이스 구조 4 4
5 5
이벤트 타입은 점(`.`)으로 구분된 계층 구조를 따른다: 6 6
7 7
``` 8 8
{namespace}.{category}.{action} 9 9
``` 10 10
11 11
본 규격은 다음 최상위 네임스페이스를 정의한다: 12 12
13 13
| 네임스페이스 | 방향 | 설명 | 14 14
| ------------ | --------------- | ----------------------------------- | 15 15
| `agent` | Client → Master | 에이전트의 출력, 상태, 요청 | 16 16
| `control` | Master → Client | 사용자 입력, 제어 명령 | 17 17
| `tool` | Client → Master | 도구 호출 및 결과 | 18 18
| `session` | 양방향 | 세션 수준 이벤트 (이력, 압축, 에러) | 19 19
20
### 3.1.1. 확장 네임스페이스 20
#### 3.1.1. 확장 네임스페이스 21 21
22 22
본 규격에 정의되지 않은 커스텀 이벤트는 `x-{vendor}` 접두사를 사용해야 한다 (MUST). 23 23
24 24
``` 25 25
x-mycompany.custom.event_name 26 26
``` 27 27
28 28
수신자는 `x-` 접두 네임스페이스를 알 수 없는 타입으로 취급하여 무시할 수 있다. 29 29
30
### 3.2. 레거시 타입 매핑 31
32
RAWP-1.0-Legacy의 이벤트 타입과 본 규격 타입 간 대응 관계: 30
### 3.2. 레거시 타입 매핑 (지원 종료) 33 31
34
| RAWP-1.0-Legacy | RAWP-DPS 1.0 | 비고 | 35
| ---------------------------------------------- | -------------------------------------- | ------------------- | 36
| `agent_session_history` | `session.history` | | 37
| `agent_stdin_write` / `agent_prompt_req` | `control.prompt.request` | | 38
| `agent_text_stream` / `agent_prompt_res` | `agent.text.delta` + `agent.text.done` | 분리 | 39
| `agent_approval_req` / `agent_interaction_ask` | `agent.interaction.request` | | 40
| `agent_approval_res` / `agent_interaction_ans` | `control.interaction.response` | | 41
| `agent_interaction_timeout` | `control.interaction.timeout` | | 42
| `agent_usage_metrics` | `session.usage` | | 43
| `agent_error` | `agent.error` | | 44
| `agent_file_transfer` | `agent.file.transfer` | | 45
| (해당 없음) | `control.file.search` | 신규, Legacy 미지원 | 46
| (해당 없음) | `session.file.candidates` | 신규, Legacy 미지원 | 47
| (해당 없음) | `control.file.search.cancel` | 신규, Legacy 미지원 | 32
RAWP-1.0-Legacy 이벤트 타입과의 매핑은 지원이 종료되었다. 본 규격의 이벤트 타입만을 사용해야 한다 (MUST). 48 33
49 34
--- 수정됨
4. 에이전트 출력 이벤트 (`agent.*`)
추가된 줄 10 삭제된 줄 10
1 1
## 4. 에이전트 출력 이벤트 (`agent.*`) 2 2
3 3
### 4.1. 텍스트 스트리밍 4 4
5 5
에이전트의 자연어 응답을 실시간으로 전달한다. 6 6
7
### 4.1.1. `agent.text.delta` (Client → Master) 7
#### 4.1.1. `agent.text.delta` (Client → Master) 8 8
9 9
```json 10 10
{ 11 11
"type": "agent.text.delta", 12 12
"payload": { 13 13
"content_index": "Number (필수, 0-based, 현재 Content Block 순번)", 14 14
"text": "String (필수, 텍스트 조각)" 15 15
} 16 16
} 17 17
``` 18 18
19
### 4.1.2. `agent.text.done` (Client → Master) 19
#### 4.1.2. `agent.text.done` (Client → Master) 20 20
21 21
텍스트 스트리밍이 완료되었음을 알린다. 하나의 Content Block이 종료될 때마다 발송한다. 22 22
23 23
```json 24 24
{ 25 25
"type": "agent.text.done", 26 26
"payload": { 27 27
"content_index": "Number (필수)", 28 28
"full_text": "String (선택, 전체 텍스트. 수신 측 검증용)" 29 29
} 30 30
} 31 31
``` 32 32
33 33
### 4.2. 추론/사고 스트리밍 (Thinking) 34 34
35 35
에이전트의 내부 추론 과정을 전달한다. Extended Thinking, Chain-of-Thought 등에 사용. 36 36
37
### 4.2.1. `agent.thinking.delta` (Client → Master) 37
#### 4.2.1. `agent.thinking.delta` (Client → Master) 38 38
39 39
```json 40 40
{ 41 41
"type": "agent.thinking.delta", 42 42
"payload": { 43 43
"content_index": "Number (필수)", 44 44
"text": "String (필수, 사고 조각)", 45 45
"redacted": "Boolean (선택, 기본값 false. 보안상 수정된 내용 여부)" 46 46
} 47 47
} 48 48
``` 49 49
50
### 4.2.2. `agent.thinking.done` (Client → Master) 50
#### 4.2.2. `agent.thinking.done` (Client → Master) 51 51
52 52
```json 53 53
{ 54 54
"type": "agent.thinking.done", 55 55
"payload": { 56 56
"content_index": "Number (필수)" 57 57
} 58 58
} 59 59
``` 60 60
61 61
### 4.3. 상호작용 요청 (Interaction Request) 62 62
63
에이전트가 사용자의 승인 또는 선택을 요구할 때 발송한다. RAWP-1.0-Legacy의 `agent_approval_req`/`agent_interaction_ask`를 통합 확장한다. 63
에이전트가 사용자의 승인 또는 선택을 요구할 때 발송한다. 64 64
65
### 4.3.1. `agent.interaction.request` (Client → Master) 65
#### 4.3.1. `agent.interaction.request` (Client → Master) 66 66
67 67
```json 68 68
{ 69 69
"type": "agent.interaction.request", 70 70
"payload": { 71 71
"interaction_id": "String (필수, UUID v4)", 72 72
"interaction_type": "String (필수, 아래 열거 값)", 73 73
"question_text": "String (필수, 사용자에게 표시할 질문)", 74 74
"timeout_sec": "Number (필수, 응답 대기 한계 시간)", 75 75
"options": [ 76 76
{ 77 77
"value": "String (필수, 시스템 전달용 식별자)", 78 78
"label": "String (필수, 사용자 노출용 텍스트)", 79 79
"description": "String (선택, 부가 설명)", 80 80
"risk_level": "String (선택, 'safe' | 'moderate' | 'dangerous')" 81 81
} 82 82
], 83 83
"context": { 84 84
"tool_name": "String (선택, 승인이 필요한 도구명)", 85 85
"tool_input": "Object (선택, 도구 입력 요약)", 86 86
"affected_paths": ["String (선택, 영향받는 파일 경로 목록)"] 87 87
}, 88 88
"default_value": "String (선택, 타임아웃 시 자동 선택될 값)" 89 89
} 90 90
} 91 91
``` 92 92
93 93
**`interaction_type` 열거 값:** 94 94
95 95
| 값 | 설명 | 96 96
| -------------- | -------------------------------------------------- | 97 97
| `YN` | 예/아니오 이진 선택 | 98 98
| `SELECT` | 다중 옵션 중 단일 선택 | 99 99
| `MULTI_SELECT` | 다중 옵션 중 복수 선택 | 100 100
| `TEXT_INPUT` | 사용자 자유 텍스트 입력 | 101 101
| `PERMISSION` | 도구 실행 권한 승인 (Claude Code의 도구 승인 흐름) | 102 102
103 103
### 4.4. 에러 보고 104 104
105
### 4.4.1. `agent.error` (Client → Master) 105
#### 4.4.1. `agent.error` (Client → Master) 106 106
107 107
```json 108 108
{ 109 109
"type": "agent.error", 110 110
"payload": { 111 111
"error_code": "String (필수)", 112 112
"message": "String (필수, 휴먼 리더블 에러)", 113 113
"severity": "String (필수, 'fatal' | 'error' | 'warning')", 114 114
"recoverable": "Boolean (필수, 에이전트가 자체 복구 가능한지 여부)", 115 115
"details": { 116 116
"module": "String (선택)", 117 117
"trace": "String (선택, 스택 트레이스)", 118 118
"related_tool": "String (선택, 에러 유발 도구명)", 119 119
"related_invocation_id": "String (선택, 에러 유발 도구 호출 ID)" 120 120
} 121 121
} 122 122
} 123 123
``` 124 124
125 125
`severity`가 `fatal`이면 클라이언트는 프로세스를 정리해야 한다 (MUST). `warning`이면 세션을 유지한다. 126 126
127 127
### 4.5. 파일 전송 128 128
129
### 4.5.1. `agent.file.transfer` (양방향) 129
#### 4.5.1. `agent.file.transfer` (양방향) 130 130
131
RAWP-1.0-Legacy와 동일하게, 바이너리는 WSS를 태우지 않고 HTTP 스토리지 URL로 교환한다. 131
바이너리는 WSS를 태우지 않고 HTTP 스토리지 URL로 교환한다. 132 132
133 133
```json 134 134
{ 135 135
"type": "agent.file.transfer", 136 136
"payload": { 137 137
"transfer_id": "String (필수, UUID v4)", 138 138
"direction": "String (필수, 'upload' | 'download')", 139 139
"file_url": "String (필수, 스토리지 URI)", 140 140
"file_name": "String (필수)", 141 141
"mime_type": "String (필수, IANA MIME 타입)", 142 142
"size_bytes": "Number (필수)", 143 143
"checksum": "String (선택, SHA-256 해시)", 144 144
"context": { 145 145
"workspace_relative_path": "String (선택, 워크스페이스 기준 상대 경로)", 146 146
"related_tool_invocation_id": "String (선택)" 147 147
} 148 148
} 149 149
} 150 150
``` 151 151
152 152
### 4.6. 에이전트 상태 변경 153 153
154
### 4.6.1. `agent.state.changed` (Client → Master) 154
#### 4.6.1. `agent.state.changed` (Client → Master) 155 155
156 156
에이전트의 내부 상태 전이를 마스터에 통보한다. 157 157
158 158
```json 159 159
{ 160 160
"type": "agent.state.changed", 161 161
"payload": { 162 162
"previous_state": "String (필수)", 163 163
"current_state": "String (필수)", 164 164
"reason": "String (선택, 전이 사유)" 165 165
} 166 166
} 167 167
``` 168 168
169 169
**예약된 상태 값**: `idle`, `thinking`, `tool_calling`, `awaiting_input`, `error` 170 170
171 171
확장 상태는 `x-{vendor}.{state_name}` 형식을 따른다. 172 172
173 173
--- 수정됨
5. 도구 호출 이벤트 (`tool.*`)
추가된 줄 57 삭제된 줄 5
1 1
## 5. 도구 호출 이벤트 (`tool.*`) 2 2
3 3
에이전트가 도구(Tool)를 호출하고 그 결과를 보고하는 이벤트 군이다. Claude Code의 Bash, Read, Write, Edit, MultiEdit, Glob, Grep, LS, WebFetch, WebSearch, NotebookRead, NotebookEdit, TodoRead, TodoWrite, Task(Subagent) 등 **모든** 도구 호출을 통일된 구조로 표현한다. 4 4
5 5
### 5.1. 설계 원칙 6 6
7 7
도구 호출 이벤트는 **도구 비종속(Tool-Agnostic)** 설계를 따른다. 즉, 프로토콜은 도구의 이름, 입력 스키마, 출력 형태를 사전에 정의하지 않으며, 임의의 도구를 동일한 Envelope으로 수용한다. 도구별 의미론은 `tool_name`과 `input`/`output` 필드의 내용으로 결정된다. 8 8
9 9
### 5.2. 도구 호출 요청 10 10
11
### 5.2.1. `tool.invoke.request` (Client → Master) 11
#### 5.2.1. `tool.invoke.request` (Client → Master) 12 12
13 13
에이전트가 도구를 호출하려 함을 마스터에 통보한다. 승인이 필요한 도구의 경우 `agent.interaction.request`가 선행한다. 14 14
15 15
```json 16 16
{ 17 17
"type": "tool.invoke.request", 18 18
"payload": { 19 19
"invocation_id": "String (필수, UUID v4, 이 호출의 고유 식별자)", 20 20
"tool_name": "String (필수, 도구명. 예: 'Bash', 'Read', 'Edit', 'TodoWrite')", 21 21
"tool_version": "String (선택, 도구 버전)", 22 22
"input": "Object (필수, 도구별 입력 파라미터)", 23 23
"parallel_group_id": "String (선택, 병렬 호출 그룹 ID. 동시에 발송되는 도구 호출을 그룹화)" 24 24
} 25 25
} 26 26
``` 27 27
28 28
### 5.3. 도구 실행 결과 29 29
30
### 5.3.1. `tool.invoke.result` (Client → Master) 30
#### 5.3.1. `tool.invoke.result` (Client → Master) 31 31
32 32
도구 실행이 완료된 후 결과를 보고한다. 33 33
34 34
```json 35 35
{ 36 36
"type": "tool.invoke.result", 37 37
"payload": { 38 38
"invocation_id": "String (필수, 대응하는 tool.invoke.request의 invocation_id)", 39 39
"tool_name": "String (필수)", 40 40
"status": "String (필수, 'success' | 'error' | 'timeout' | 'cancelled')", 41 41
"output": "Any (필수, 도구별 출력. 구조는 도구 의존적)", 42 42
"output_type": "String (필수, 출력 해석 힌트. 아래 열거 값)", 43 43
"duration_ms": "Number (선택, 실행 소요 시간 밀리초)", 44 44
"truncated": "Boolean (선택, 출력이 잘렸는지 여부)" 45 45
} 46 46
} 47 47
``` 48 48
49 49
**`output_type` 열거 값:** 50 50
51 51
| 값 | 설명 | 대표 도구 | 52 52
| -------------- | --------------------------------------------- | ------------------------- | 53 53
| `text` | 평문 텍스트 | Bash stdout, Grep 결과 | 54 54
| `structured` | JSON 구조체 | TodoRead, Agent Discovery | 55 55
| `file_content` | 파일 본문 (라인 번호 포함 가능) | Read, NotebookRead | 56 56
| `file_list` | 파일 경로 목록 | Glob, LS | 57 57
| `diff` | 변경사항 차분 (unified diff 또는 구조화 diff) | Edit, MultiEdit 결과 | 58 58
| `web_content` | 웹 페이지/검색 결과 | WebFetch, WebSearch | 59 59
| `empty` | 출력 없음 (부수효과만 발생) | Write, NotebookEdit | 60 60
| `binary_ref` | 바이너리 참조 (agent.file.transfer 연계) | 이미지 생성 등 | 61 61
| `agent_result` | 서브에이전트 실행 결과 | Task(Subagent) | 62 62
63
### 5.3.2. `tool.invoke.stream` (Client → Master) 63
#### 5.3.2. `tool.invoke.stream` (Client → Master) 64 64
65 65
장시간 실행 도구의 중간 출력을 스트리밍한다 (예: Bash 명령의 실시간 stdout). 66 66
67 67
```json 68 68
{ 69 69
"type": "tool.invoke.stream", 70 70
"payload": { 71 71
"invocation_id": "String (필수)", 72 72
"tool_name": "String (필수)", 73 73
"stream_type": "String (필수, 'stdout' | 'stderr' | 'progress')", 74 74
"chunk": "String (필수, 출력 조각)", 75 75
"sequence": "Number (필수, 0-based 순번, 순서 재구성용)" 76 76
} 77 77
} 78 78
``` 79 79
80
#### 5.3.3. `output_type` 결정 규칙 81
82
`tool.invoke.result`의 `output_type` 값은 다음 우선순위에 따라 결정해야 한다 (MUST): 83
84
1. **도구 카탈로그 사전 선언**: `tool.catalog.publish`(§5.4.1)에서 해당 도구의 `output_type`이 사전 선언된 경우, 해당 값을 사용한다. 이것이 최우선이다. 85
2. **도구 카테고리 기반 기본값**: 사전 선언이 없으면, `tool.catalog.publish`의 `category` 필드에 따른 기본값을 적용한다: 86
87
| `category` | 도구 동작 | 기본 `output_type` | 88
| ----------------- | --------------------------------------- | ---------------------------------- | 89
| `filesystem` | 파일 쓰기/수정 (Write, Edit, MultiEdit) | `diff` (변경 발생 시) 또는 `empty` | 90
| `filesystem` | 파일 읽기 (Read, NotebookRead) | `file_content` | 91
| `filesystem` | 파일 탐색 (Glob, LS) | `file_list` | 92
| `shell` | 셸 명령 실행 (Bash) | `text` | 93
| `search` | 검색 (Grep, WebSearch) | `text` 또는 `file_list` | 94
| `web` | 웹 콘텐츠 접근 (WebFetch) | `web_content` | 95
| `task_management` | 태스크 관리 (TodoRead, TodoWrite) | `structured` | 96
| `agent` | 서브에이전트 위임 (Task) | `agent_result` | 97
98
3. **폴백**: 위 규칙으로 결정할 수 없으면 `text`로 설정한다 (MUST). 99
100
`output_type`은 수신 측(마스터, 엣지 노드)이 결과를 적절한 뷰어로 렌더링하기 위한 힌트이다. 각 `output_type`의 렌더링 규칙은 RAWP-CRS 1.0.1 §4.5를 참조한다. 101
80 102
### 5.4. 도구 사용 가능 목록 고지 (Tool Catalog) 81 103
82
### 5.4.1. `tool.catalog.publish` (Client → Master) 104
#### 5.4.1. `tool.catalog.publish` (Client → Master) 83 105
84 106
에이전트가 현재 세션에서 사용 가능한 도구 목록을 마스터에 고지한다. 세션 시작 직후 또는 도구 구성 변경 시 발송한다. 85 107
86 108
```json 87 109
{ 88 110
"type": "tool.catalog.publish", 89 111
"payload": { 90 112
"tools": [ 91 113
{ 92 114
"name": "String (필수, 도구명)", 93 115
"version": "String (선택)", 94 116
"description": "String (필수, 도구 설명)", 95 117
"input_schema": "Object (선택, JSON Schema)", 96 118
"output_type": "String (선택, 기본 output_type)", 97 119
"requires_approval": "Boolean (선택, 실행 전 승인 필요 여부)", 98
"category": "String (선택, 도구 분류. 아래 열거 값)" 120
"category": "String (선택, 도구 분류. 아래 열거 값)", 121
"status": "String (선택, 기본값 'available'. 아래 열거 값)", 122
"status_description": "String (선택, 현재 상태에 대한 사유 설명. 없으면 UI에 상태 사유를 표시하지 않는다)" 99 123
} 124
], 125
"diagnostics": [ 126
{ 127
"severity": "String (필수, 'info' | 'warning' | 'error' | 'fatal')", 128
"tool_name": "String (선택, 특정 도구에 관한 진단이면 해당 도구명)", 129
"code": "String (필수, 진단 코드. 예: 'TOOL_UNAVAILABLE', 'VERSION_MISMATCH', 'DEPENDENCY_MISSING')", 130
"message": "String (필수, 휴먼 리더블 진단 메시지)" 131
} 100 132
] 101 133
} 102 134
} 103 135
``` 104 136
105 137
**`category` 열거 값:** 106 138
107 139
| 값 | 설명 | 예시 | 108 140
| ----------------- | ----------------- | -------------------------------------- | 109 141
| `filesystem` | 파일 시스템 조작 | Read, Write, Edit, MultiEdit, Glob, LS | 110 142
| `shell` | 셸 명령 실행 | Bash | 111 143
| `search` | 검색 | Grep, WebSearch | 112 144
| `web` | 웹 콘텐츠 접근 | WebFetch | 113 145
| `notebook` | 노트북 조작 | NotebookRead, NotebookEdit | 114 146
| `task_management` | 태스크/TODO 관리 | TodoRead, TodoWrite | 115 147
| `agent` | 서브에이전트 위임 | Task | 116 148
| `planning` | 계획 모드 | EnterPlanMode, ExitPlanMode | 117 149
| `mcp` | MCP 서버 도구 | 동적 MCP 도구 | 118 150
| `custom` | 사용자 정의 도구 | 커스텀 도구 | 119 151
152
**`status` 열거 값:** 153
154
| 값 | 설명 | 155
| ------------- | ---------------------------------------------------------------------------------------------------------------------------------------------- | 156
| `available` | 도구가 정상 동작 가능. 기본값. `status` 필드가 생략되면 `available`로 간주한다. | 157
| `unavailable` | 도구를 사용할 수 없음. 의존성 누락, 권한 부족, 실행 파일 미설치 등. 마스터는 이 도구에 대한 `tool.invoke.request` 발송을 차단해야 한다 (MUST). | 158
| `degraded` | 도구가 동작하지만 일부 기능이 제한됨. 사용은 허용되나 사용자에게 제한 사항을 고지해야 한다 (SHOULD). | 159
160
`status`가 `unavailable` 또는 `degraded`인 도구는 `status_description`에 사유를 명시하는 것을 권장한다 (SHOULD). 예: `"실행 파일을 찾을 수 없음"`, `"API 키 미설정"`. `status_description`이 없으면 UI에 상태 사유를 표시하지 않는다. 161
162
도구 구성이 변경되어 `status`가 전이되면(예: 의존성 설치로 `unavailable` → `available`) 클라이언트는 `tool.catalog.publish`를 재발송해야 한다 (MUST). 163
164
**`diagnostics` 필드 규칙:** 165
166
- `diagnostics` 배열은 선택적이다 (OPTIONAL). 진단 사항이 없으면 생략하거나 빈 배열을 전송할 수 있다. 167
- `severity: "fatal"` 진단이 포함된 도구는 마스터가 해당 도구의 사용을 차단해야 한다 (MUST). 해당 도구에 대한 `tool.invoke.request`가 수신되면 `session.error`를 반환한다. 168
- `severity: "error"` 진단이 포함된 도구는 마스터가 사용자에게 경고를 표시해야 한다 (SHOULD). 도구 사용 자체는 허용된다. 169
- `severity: "warning"` 또는 `"info"` 진단은 로그 기록 또는 UI 표시용이며, 도구 사용에 제한을 두지 않는다. 170
- `tool_name`이 생략된 진단은 도구 카탈로그 전체에 대한 진단이다 (예: 에이전트 초기화 경고). 171
120 172
--- 수정됨
6. 제어 이벤트 (`control.*`)
추가된 줄 46 삭제된 줄 8
1 1
## 6. 제어 이벤트 (`control.*`) 2 2
3 3
마스터 서버가 클라이언트(에이전트)로 전달하는 제어 명령이다. 4 4
5 5
### 6.1. 프롬프트/입력 전달 6 6
7
### 6.1.1. `control.prompt.request` (Master → Client) 7
#### 6.1.1. `control.prompt.request` (Master → Client) 8 8
9 9
```json 10 10
{ 11 11
"type": "control.prompt.request", 12 12
"payload": { 13 13
"prompt_text": "String (필수, 사용자 입력. 파일 참조 토큰 포함 가능, §16.2 참조)", 14 14
"input_type": "String (선택, 기본값 'text'. 아래 열거 값)", 15 15
"context_files": [ 16 16
{ 17 17
"file_url": "String (필수, 참조 파일 URL)", 18 18
"file_name": "String (필수)", 19 19
"mime_type": "String (선택)", 20 20
"intent": "String (선택, 'reference' | 'edit_target' | 'context')" 21 21
} 22 22
], 23 23
"file_references": [ 24 24
{ 25 25
"ref_id": "String (필수, prompt_text 내 토큰의 ref_id와 일치)", 26 26
"path": "String (필수, workspace 기준 상대 경로)", 27 27
"name": "String (필수, 파일명)", 28 28
"extension": "String (선택, 확장자)", 29 29
"mime_type": "String (선택, IANA MIME 타입)", 30 30
"size_bytes": "Number (선택)", 31 31
"workspace_relative_path": "String (필수, 워크스페이스 기준 상대 경로)" 32 32
} 33 33
], 34 34
"config_overrides": { 35 35
"output_format": "String (선택, 'text' | 'json' | 'stream-json')", 36 36
"json_schema": "Object (선택, 구조화 출력 스키마)", 37 37
"max_turns": "Number (선택, 최대 Turn 수 제한)", 38 38
"allowed_tools": ["String (선택, 허용 도구 목록)"], 39 39
"denied_tools": ["String (선택, 금지 도구 목록)"] 40 40
} 41 41
} 42 42
} 43 43
``` 44 44
45
> **변경 사항**: `file_references` 필드가 추가되었다. 이 필드는 `prompt_text` 내에 삽입된 인라인 파일 참조 토큰(§16.2)의 메타데이터를 제공한다. `file_references`와 기존 `context_files`의 관계: `context_files`는 프롬프트와 함께 첨부되는 참조 파일이고, `file_references`는 프롬프트 텍스트 내에서 인라인으로 언급된 파일의 메타데이터이다. 양자는 독립적이며, 동일 파일이 양쪽에 모두 존재할 수 있다. 46
47 45
**`input_type` 열거 값:** 48 46
49 47
| 값 | 설명 | 50 48
| --------------- | ------------------------------------- | 51 49
| `text` | 일반 텍스트 프롬프트 | 52 50
| `slash_command` | 슬래시 명령 (예: `/compact`, `/cost`) | 53 51
| `continuation` | 이전 Turn에 이어지는 후속 입력 | 54 52
| `piped_stdin` | 파이프된 표준 입력 (headless 모드) | 55 53
56
### 6.1.2. `control.prompt.cancel` (Master → Client) 54
#### 6.1.2. `control.prompt.cancel` (Master → Client) 57 55
58 56
진행 중인 에이전트 Turn을 취소한다. 수신 시 클라이언트는 현재 실행 중인 도구를 가능한 한 빨리 중단하고, `agent.text.done`(is_cancelled: true)을 발송해야 한다 (MUST). 59 57
60 58
```json 61 59
{ 62 60
"type": "control.prompt.cancel", 63 61
"payload": { 64 62
"target_turn_id": "String (필수, 취소 대상 turn_id)", 65 63
"reason": "String (선택, 'user_cancelled' | 'timeout' | 'quota_exceeded')" 66 64
} 67 65
} 68 66
``` 69 67
70 68
### 6.2. 상호작용 응답 71 69
72
### 6.2.1. `control.interaction.response` (Master → Client) 70
#### 6.2.1. `control.interaction.response` (Master → Client) 73 71
74 72
```json 75 73
{ 76 74
"type": "control.interaction.response", 77 75
"payload": { 78 76
"interaction_id": "String (필수, 대응하는 agent.interaction.request의 interaction_id)", 79 77
"selected_value": "String | [String] (필수, 단일 선택 또는 복수 선택 값)", 80 78
"text_input": "String (선택, TEXT_INPUT 타입의 사용자 자유 입력)" 81 79
} 82 80
} 83 81
``` 84 82
85
### 6.2.2. `control.interaction.timeout` (Master → Client) 83
#### 6.2.2. `control.interaction.timeout` (Master → Client) 86 84
87 85
```json 88 86
{ 89 87
"type": "control.interaction.timeout", 90 88
"payload": { 91 89
"interaction_id": "String (필수)" 92 90
} 93 91
} 94 92
``` 95 93
96 94
### 6.3. 세션 제어 명령 97 95
98
### 6.3.1. `control.session.compact` (Master → Client) 96
#### 6.3.1. `control.session.compact` (Master → Client) 99 97
100 98
마스터가 컨텍스트 압축을 요청한다 (사용자의 `/compact` 명령 등). 101 99
102 100
```json 103 101
{ 104 102
"type": "control.session.compact", 105 103
"payload": { 106 104
"instruction": "String (선택, 압축 시 보존할 내용 지시)", 107 105
"target_token_ratio": "Number (선택, 0.0–1.0, 목표 압축 비율)" 108 106
} 109 107
} 110 108
``` 111 109
112
### 6.3.2. `control.mode.switch` (Master → Client) 110
**자동 압축 트리거 조건:** 113 111
112
에이전트는 `session.capabilities`에서 `context_compaction: true`를 고지한 경우에 한하여 자동 압축을 수행할 수 있다 (MAY). 자동 압축의 트리거 조건은 다음을 따른다: 113
114
- `context_window.utilization` ≥ 0.80이면 압축을 고려해야 한다 (SHOULD). 115
- `context_window.utilization` ≥ 0.95이면 압축을 수행해야 한다 (MUST). 116
- 자동 압축 시 `session.compacted`(§7.2.1)의 `trigger` 필드는 `"auto"`로 설정한다. 117
- 마스터의 `control.session.compact` 없이 에이전트가 자체적으로 수행한다. 118
119
**`target_token_ratio` 필드 의미:** 120
121
- `target_token_ratio`는 압축 후 목표 컨텍스트 윈도우 사용률을 의미한다. 값 범위: `0.0`(최대 압축) ~ `1.0`(압축하지 않음). 122
- 예: `target_token_ratio: 0.5`이면 컨텍스트 윈도우의 50% 이하로 압축을 목표로 한다. 123
- 이 값은 best-effort이다. 에이전트가 정확한 비율 달성을 보장하지 않는다. 124
- 필드가 생략되면 에이전트의 기본 압축 전략을 따른다. 125
126
**압축 미지원 시 동작:** 127
128
`session.capabilities`에서 `context_compaction: false`이거나 미고지인 클라이언트가 `control.session.compact`를 수신하면, `session.error` (error_code: `"COMPACTION_NOT_SUPPORTED"`, fatal: `false`)를 반환해야 한다 (MUST). 129
130
#### 6.3.2. `control.mode.switch` (Master → Client) 131
114 132
에이전트 동작 모드를 전환한다. 115 133
116 134
```json 117 135
{ 118 136
"type": "control.mode.switch", 119 137
"payload": { 120 138
"mode": "String (필수, 아래 열거 값)", 121 139
"config": "Object (선택, 모드별 추가 설정)" 122 140
} 123 141
} 124 142
``` 125 143
126 144
**`mode` 열거 값:** 127 145
128 146
| 값 | 설명 | 129 147
| --------- | -------------------------------------------- | 130 148
| `default` | 기본 실행 모드 (읽기/쓰기/실행 모두 가능) | 131 149
| `plan` | 계획 모드 (읽기/검색만 허용, 쓰기/실행 차단) | 132 150
| `review` | 코드 리뷰 모드 | 133 151
152
**모드별 도구 사용 제한:** 153
154
| 모드 | 허용 도구 카테고리 | 금지 도구 카테고리 | 155
| --------- | -------------------------------------------------------------- | --------------------------------------------------- | 156
| `default` | 모든 카테고리 | 없음 | 157
| `plan` | `search`, `filesystem`(읽기 전용: Read, Glob, LS, Grep), `web` | `filesystem`(쓰기: Write, Edit, MultiEdit), `shell` | 158
| `review` | `search`, `filesystem`(읽기 전용), `web` | `filesystem`(쓰기), `shell` | 159
160
금지된 카테고리의 도구가 에이전트에 의해 호출 시도되면, 클라이언트는 해당 `tool.invoke.request`를 차단하고 `tool.invoke.result` (status: `"error"`, output: `"Tool not allowed in current mode"`)를 에이전트에 반환해야 한다 (MUST). 161
162
**모드 전환 규칙:** 163
164
- Turn 진행 중(`session.turn.start` 수신 후 `session.turn.end` 수신 전)에는 모드를 전환할 수 없다 (MUST NOT). 진행 중에 `control.mode.switch`를 수신하면 `session.error` (error_code: `"MODE_SWITCH_DURING_TURN"`, fatal: `false`)를 반환해야 한다 (MUST). 165
- 모드 전환 성공 시 `agent.state.changed` (reason: `"mode_switch"`)를 발송해야 한다 (MUST). 166
- 전환 후 첫 Turn의 `session.turn.start`에 변경된 `mode` 필드를 포함해야 한다 (MUST). 167
168
**미지원 시 동작:** 169
170
`session.capabilities`에서 `plan_mode: false`이거나 미고지인 클라이언트가 `control.mode.switch`를 수신하면, `session.error` (error_code: `"MODE_NOT_SUPPORTED"`, fatal: `false`)를 반환해야 한다 (MUST). 마스터는 `plan_mode` 미지원 클라이언트에 `control.mode.switch`를 발송해서는 안 된다 (MUST NOT). 171
134 172
--- 수정됨
7. 세션 이벤트 (`session.*`)
추가된 줄 112 삭제된 줄 8
1 1
## 7. 세션 이벤트 (`session.*`) 2 2
3 3
### 7.1. 이력 복구 4 4
5
### 7.1.1. `session.history` (Client → Master) 5
#### 7.1.1. `session.history` (Client → Master) 6 6
7
재연결(reattach) 성공 직후 선제적으로 발송한다. RAWP-1.0-Legacy의 `agent_session_history`를 대체한다. 7
재연결(reattach) 성공 직후 선제적으로 발송한다. 8 8
9 9
```json 10 10
{ 11 11
"type": "session.history", 12 12
"payload": { 13 13
"frames": [ 14 14
"Object[] (필수, 단절 동안 버퍼에 쌓인 과거 프레임의 배열. 각 요소는 본 규격의 Envelope 전체)" 15 15
], 16 16
"buffer_status": { 17 17
"policy_applied": "String (필수, 'RING' | 'DROP' | 'NONE')", 18 18
"truncated": "Boolean (필수, 데이터 유실 발생 여부)", 19 19
"lost_frame_count": "Number (선택, 유실된 프레임 수 추정치)", 20 20
"buffer_high_watermark": "Number (선택, 버퍼 최대 사용량 바이트)" 21 21
}, 22 22
"last_sync_timestamp": "String (필수, ISO 8601, 마지막 동기화 시각)", 23 23
"last_message_id": "String (필수, 마지막 동기화 message_id)" 24 24
} 25 25
} 26 26
``` 27 27
28 28
`buffer_status.truncated`가 `true`이면 마스터는 데이터 불완전성을 사용자에게 고지해야 한다 (MUST). 29 29
30 30
### 7.2. 컨텍스트 압축 보고 31 31
32
### 7.2.1. `session.compacted` (Client → Master) 32
#### 7.2.1. `session.compacted` (Client → Master) 33 33
34 34
에이전트가 자동 또는 수동 컨텍스트 압축을 수행한 후 결과를 보고한다. 35 35
36 36
```json 37 37
{ 38 38
"type": "session.compacted", 39 39
"payload": { 40 40
"summary": "String (필수, 압축 요약 텍스트)", 41 41
"previous_token_count": "Number (필수, 압축 전 토큰 수)", 42 42
"current_token_count": "Number (필수, 압축 후 토큰 수)", 43 43
"preserved_elements": { 44 44
"files_modified": ["String (선택, 수정된 파일 경로)"], 45 45
"decisions_made": ["String (선택, 주요 결정 사항)"], 46 46
"errors_encountered": ["String (선택, 미해결 에러)"], 47 47
"active_todos": ["Object (선택, 활성 TODO 항목)"] 48 48
}, 49 49
"trigger": "String (필수, 'auto' | 'manual' | 'requested')" 50 50
} 51 51
} 52 52
``` 53 53
54 54
### 7.3. 사용량 지표 55 55
56
### 7.3.1. `session.usage` (양방향, 주로 Client → Master) 56
#### 7.3.1. `session.usage` (양방향, 주로 Client → Master) 57 57
58
텍스트 생성 완료 시점(`agent.text.done`) 또는 Turn 종료 시 반드시 동반 발송해야 한다 (MUST). RAWP-1.0-Legacy의 `agent_usage_metrics`를 대체한다. 58
텍스트 생성 완료 시점(`agent.text.done`) 또는 Turn 종료 시 반드시 동반 발송해야 한다 (MUST). 59 59
60 60
```json 61 61
{ 62 62
"type": "session.usage", 63 63
"payload": { 64 64
"turn_id": "String (선택, 사용량이 소속된 Turn)", 65 65
"token_usage": { 66 66
"input_tokens": "Number (필수)", 67 67
"output_tokens": "Number (필수)", 68 68
"cache_read_tokens": "Number (선택, 캐시 히트 토큰)", 69 69
"cache_write_tokens": "Number (선택, 캐시 기록 토큰)", 70 70
"thinking_tokens": "Number (선택, 사고 토큰)" 71 71
}, 72 72
"cost_usage": { 73 73
"limit": "Number (필수, -1은 무제한)", 74 74
"used": "Number (필수)", 75 75
"unit": "String (필수, 'USD' 등)" 76 76
}, 77 77
"message_usage": { 78 78
"limit": "Number (필수)", 79 79
"used": "Number (필수)", 80 80
"unit": "String (필수, 'COUNT')" 81 81
}, 82 82
"context_window": { 83 83
"capacity": "Number (선택, 최대 토큰 윈도우)", 84 84
"used": "Number (선택, 현재 사용 중 토큰)", 85 85
"utilization": "Number (선택, 0.0–1.0, 사용률)" 86 86
}, 87 87
"time_to_reset": "String (필수, ISO 8601)" 88 88
} 89 89
} 90 90
``` 91 91
92 92
### 7.4. 세션 에러 93 93
94
### 7.4.1. `session.error` (양방향) 94
#### 7.4.1. `session.error` (양방향) 95 95
96 96
세션 수준의 프로토콜 에러를 보고한다. 에이전트 수준 에러(`agent.error`)와 구분된다. 97 97
98 98
```json 99 99
{ 100 100
"type": "session.error", 101 101
"payload": { 102 102
"error_code": "String (필수)", 103 103
"message": "String (필수)", 104 104
"fatal": "Boolean (필수, true 시 세션 종료 절차 개시)" 105 105
} 106 106
} 107 107
``` 108 108
109 109
### 7.5. Turn 라이프사이클 110 110
111
### 7.5.1. `session.turn.start` (Client → Master) 111
#### 7.5.1. `session.turn.start` (Client → Master) 112 112
113 113
에이전트가 새 Turn을 시작함을 알린다. 114 114
115 115
```json 116 116
{ 117 117
"type": "session.turn.start", 118 118
"payload": { 119 119
"turn_id": "String (필수, UUID v4)", 120 120
"turn_index": "Number (필수, 0-based 순번)", 121 121
"mode": "String (선택, 현재 동작 모드)" 122 122
} 123 123
} 124 124
``` 125 125
126
### 7.5.2. `session.turn.end` (Client → Master) 126
#### 7.5.2. `session.turn.end` (Client → Master) 127 127
128 128
Turn이 완료되었음을 알린다. 이 이벤트 직후 `session.usage`가 동반되어야 한다 (MUST). 129 129
130 130
```json 131 131
{ 132 132
"type": "session.turn.end", 133 133
"payload": { 134 134
"turn_id": "String (필수)", 135 135
"stop_reason": "String (필수, 아래 열거 값)", 136 136
"tool_invocation_count": "Number (선택, 이 Turn에서의 도구 호출 횟수)" 137 137
} 138 138
} 139 139
``` 140 140
141 141
**`stop_reason` 열거 값:** 142 142
143 143
| 값 | 설명 | 144 144
| ---------------- | ------------------------------- | 145 145
| `end_turn` | 정상 완료 | 146 146
| `max_tokens` | 최대 토큰 도달 | 147 147
| `cancelled` | 사용자/시스템 취소 | 148 148
| `error` | 에러로 인한 중단 | 149 149
| `tool_use` | 도구 사용 후 Turn 전환 (멀티턴) | 150 150
| `awaiting_input` | 사용자 입력 대기 | 151 151
152
#### 7.5.3. 세션 종료 시 진행 중 항목 처리 153
154
세션이 종료(RAWP 1.0.2 §5.2 `DELETE /v1/session/{session_id}` 또는 `session.error` (fatal: `true`))될 때, 진행 중인 Turn이 존재하면 다음 순서로 정리 프레임을 발송해야 한다 (MUST): 155
156
1. 모든 미완료 도구 호출에 대해 `tool.invoke.result` (status: `"cancelled"`)를 발송한다. 157
2. `session.turn.end` (stop_reason: `"error"`)를 발송한다. 158
3. WSS 연결을 종료한다. 159
160
위 프레임들은 WSS 연결 종료 전에 모두 발송 완료되어야 한다 (MUST). 진행 중인 Turn이 없으면 즉시 WSS를 종료한다. 161
162
### 7.6. 세션 이름 변경 163
164
#### 7.6.1. `session.renamed` (양방향) 165
166
```json 167
{ 168
"type": "session.renamed", 169
"payload": { 170
"session_id": "String (필수, 대상 세션 식별자)", 171
"name": "String (필수, 변경된 세션 이름)", 172
"previous_name": "String (선택, 변경 전 세션 이름)", 173
"renamed_by": "String (필수, 'edge' | 'client' | 'master')" 174
} 175
} 176
``` 177
178
`renamed_by`는 이름 변경을 요청한 주체를 식별한다. 마스터는 이 이벤트를 해당 세션에 연결된 모든 엣지 WSS로 브로드캐스트해야 한다 (MUST). 179
180
### 7.7. 세션 삭제 181
182
#### 7.7.1. `session.deleted` (양방향) 183
184
세션이 삭제(RAWP 1.0.2 §5.2 또는 §10.1.3)되었음을 통보한다. 마스터 또는 로컬 UI가 연결된 모든 참여자에게 세션 종료를 알리는 데 사용된다. 185
186
```json 187
{ 188
"type": "session.deleted", 189
"payload": { 190
"session_id": "String (필수, 삭제된 세션 식별자)", 191
"reason": "String (필수, 'user_request' | 'master_request' | 'timeout' | 'error')", 192
"deleted_by": "String (필수, 'edge' | 'client' | 'master' | 'system')" 193
} 194
} 195
``` 196
197
**`reason` 열거 값:** 198
199
| 값 | 설명 | 200
| ---------------- | ------------------------------------------------------ | 201
| `user_request` | 사용자가 UI에서 명시적으로 세션 삭제 | 202
| `master_request` | 마스터 서버가 `DELETE /v1/session/{session_id}`로 종료 | 203
| `timeout` | `reattach_window` 초과로 DETACHED 세션이 만료 | 204
| `error` | 복구 불가능한 에러로 세션 강제 종료 | 205
206
이 이벤트는 §7.5.3의 종료 정리 프레임 이후, WSS 연결 종료 직전에 발송해야 한다 (MUST). 마스터는 이 이벤트를 해당 세션에 연결된 모든 엣지 WSS로 브로드캐스트해야 한다 (MUST). 207
208
### 7.8. WSS 연결 단절 복구 (Disconnect Recovery) 209
210
WSS Connection이 비정상적으로 종료(네트워크 단절, 서버 셧다운 등)된 후 클라이언트 또는 엣지 노드의 미완료 메시지 정리(Finalization) 및 재연결 시 복구(Unfinalization) 절차를 정의한다. 211
212
#### 7.8.1. Finalization (미완료 메시지 정리) 213
214
WSS Connection의 `close` 또는 `error` 이벤트를 수신하면, 수신 측은 해당 세션의 미완료 상태를 다음 규칙에 따라 즉시 정리해야 한다 (MUST): 215
216
| 미완료 상태 | Finalization 동작 | 217
| ---------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | 218
| 스트리밍 중인 텍스트 블록 (`agent.text.delta` 수신 후 `agent.text.done` 미수신) | 스트리밍을 종료하고, 수신된 텍스트까지를 확정한다. | 219
| 스트리밍 중인 사고 블록 (`agent.thinking.delta` 수신 후 `agent.thinking.done` 미수신) | 사고 스트리밍을 종료한다. | 220
| 미완료 도구 호출 (`tool.invoke.request` 수신 후 `tool.invoke.result` 미수신) | 로컬에서 `tool.invoke.result` (status: `"cancelled"`, output: `"Connection lost"`)를 합성하여 쌍을 완성한다. 합성된 결과에는 `synthetic: true` 플래그를 포함해야 한다 (MUST). | 221
| 미완료 Turn (`session.turn.start` 수신 후 `session.turn.end` 미수신) | Turn을 종료 처리한다. stop_reason은 `"disconnected"`로 간주한다. | 222
| 대기 중인 상호작용 (`agent.interaction.request` 수신 후 `control.interaction.response` 미발송) | 상호작용 요청을 폐기하여 UI 블로킹을 해제한다. | 223
| 에이전트 상태 (`thinking`, `tool_calling` 등 비유휴 상태) | `idle` 상태로 전환하여 입력 가능 상태를 복원한다. | 224
225
**합성 블록의 식별**: 로컬에서 합성한 모든 블록(cancelled tool-result, 종료된 Turn 등)은 `synthetic: true` 플래그를 Envelope의 `metadata` 객체에 포함해야 한다 (MUST). 이 플래그는 §7.8.2의 Unfinalization에서 합성 블록을 정확히 식별하고 제거하기 위해 필요하다. 226
227
```json 228
{ 229
"metadata": { 230
"synthetic": true 231
} 232
} 233
``` 234
235
#### 7.8.2. Finalization 트리거 시점 236
237
다음 이벤트가 발생하면 Finalization을 수행해야 한다 (MUST): 238
239
- **WSS close/error 이벤트 수신**: 네트워크 단절, 서버 셧다운, 타임아웃 등으로 WSS Connection이 비정상 종료된 경우. 240
- **앱 재시작 후 영속 상태 복원**: 앱이 재시작되어 이전 세션의 영속 상태를 복원할 때, 해당 세션의 WSS Connection이 이미 존재하지 않는 경우. 241
- **세션 명시적 종료**: §7.5.3 및 §7.7의 절차가 우선 적용된다. 서버 측에서 정리 프레임이 정상 발송된 경우 Finalization은 불필요하다. 정리 프레임 없이 연결이 끊어진 경우에만 Finalization을 수행한다. 242
243
#### 7.8.3. Unfinalization (재연결 복구) 244
245
`reattach_window`(RAWP 1.0.2 §4.1.5) 내에 WSS 재연결(reattach)이 성공하고 `session.history`(§7.1.1)를 수신한 경우, 이전에 수행한 Finalization을 되돌려야 한다 (MUST): 246
247
1. `metadata.synthetic: true`가 포함된 로컬 합성 블록(cancelled tool-result 등)을 식별하여 제거한다. 248
2. `session.history`의 `frames` 배열에 포함된 프레임을 순서대로 재생하여 실제 서버 측 상태로 복원한다. 249
3. `session.history` 재생 완료 후, 정상 메시지 수신을 재개한다. 250
251
**Unfinalization 불가 판정**: 다음의 경우 Unfinalization을 수행하지 않고 Finalization 상태를 유지한다: 252
253
- `session.history`의 `buffer_status.truncated`가 `true`인 경우. 데이터 유실이 발생했으므로 완전한 복구가 불가능하다. 254
- `session.deleted` 이벤트가 `session.history`의 `frames`에 포함된 경우. 세션이 이미 종료되었으므로 복구 대상이 아니다. 255
152 256
--- 수정됨
10. 서브에이전트 위임 규격 (Subagent Delegation)
추가된 줄 96 삭제된 줄 14
1 1
## 10. 서브에이전트 위임 규격 (Subagent Delegation) 2 2
3
에이전트가 하위 에이전트(Task/Agent)를 생성하여 작업을 위임하는 구조를 정의한다. 3
에이전트가 하위 에이전트(서브에이전트)를 생성하여 작업을 위임하는 구조를 정의한다. 서브에이전트는 부모 에이전트와 동일한 Agent Session 내에서 실행되며, 서브에이전트 내부에서 발생하는 모든 DPS 프레임은 부모와 동일한 `session_id`를 공유한다. 4 4
5
### 10.1. 서브에이전트 시작 5
### 10.1. 서브에이전트 호출 6 6
7
`tool.invoke.request`에서 `tool_name: "Task"` 또는 `tool_name: "Agent"`를 사용하며, `input`에 서브에이전트 설정을 포함한다: 7
`tool.invoke.request`에서 서브에이전트를 생성하는 도구를 호출한다. `payload`에 `subagent` 필드가 존재하면 이 도구 호출이 서브에이전트를 생성함을 의미한다. 8 8
9 9
```json 10 10
{ 11 11
"type": "tool.invoke.request", 12 12
"payload": { 13
"invocation_id": "subagent-uuid-here", 14
"tool_name": "Task", 13
"invocation_id": "String (필수, UUID v4. 이 값이 서브에이전트의 parent_invocation_id가 된다)", 14
"tool_name": "String (필수, 예: 'Task', 'Agent')", 15 15
"input": { 16 16
"description": "String (필수, 서브에이전트 임무 설명)", 17 17
"prompt": "String (필수, 서브에이전트에게 전달할 프롬프트)", 18 18
"allowed_tools": ["String (선택, 서브에이전트 허용 도구 목록)"], 19 19
"model": "String (선택, 사용할 모델)" 20
}, 21
"subagent": { 22
"agent_type": "String (선택, 서브에이전트 유형. 예: 'Explore', 'Plan', 'general-purpose')", 23
"description": "String (선택, 서브에이전트 임무 요약)" 20 24
} 21 25
} 22 26
} 23 27
``` 24 28
25
### 10.2. 서브에이전트 출력 29
`subagent` 필드가 없으면 일반 도구 호출이다. 26 30
27
서브에이전트의 모든 출력은 부모 에이전트의 세션 내에서 `tool.invoke.stream`으로 중계되거나, 완료 시 `tool.invoke.result`(output_type: `agent_result`)로 최종 결과가 보고된다. 31
### 10.2. 서브에이전트 컨텍스트 (`metadata.subagent_context`) 28 32
29
서브에이전트의 중간 이벤트를 마스터에 직접 노출하려면, 각 프레임의 `metadata`에 위임 체인을 기록한다: 33
서브에이전트 내부에서 발생하는 모든 DPS 프레임은 `metadata.subagent_context` 필드를 포함해야 한다 (MUST). 이를 통해 수신 측은 각 메시지의 출처 에이전트를 식별할 수 있다. 30 34
31 35
```json 32 36
{ 33 37
"metadata": { 34
"delegation_chain": [ 35
{ 36
"agent_id": "String (부모 에이전트 식별자)", 37
"invocation_id": "String (위임 호출 ID)" 38
} 39
] 38
"subagent_context": { 39
"parent_invocation_id": "String (필수, 이 서브에이전트를 호출한 tool.invoke.request의 invocation_id)", 40
"agent_type": "String (선택, 서브에이전트 유형)", 41
"depth": "Number (필수, 중첩 깊이. 0 = 메인 에이전트, 1 = 서브에이전트, 2 = 서브의 서브에이전트)" 42
} 40 43
} 41 44
} 42 45
``` 43 46
47
**규칙:** 48
49
- `subagent_context`가 없거나 `null`인 프레임은 메인 에이전트(`depth: 0`)의 메시지로 취급해야 한다 (MUST). 50
- 서브에이전트 내부의 `agent.*`, `tool.*`, `session.*` 모든 프레임에 포함해야 한다 (MUST). 51
- `depth`는 발신 측(클라이언트)이 서브에이전트 생성 시점에 이미 알고 있는 값이다. 수신 측이 전체 프레임 히스토리 없이 단일 프레임만으로 중첩 깊이를 결정할 수 있도록 명시적으로 포함한다. 52
- 중첩 서브에이전트의 경우 `parent_invocation_id`는 직계 부모의 `invocation_id`만 가리킨다. 전체 위임 체인이 필요한 경우 `parent_invocation_id`를 재귀 추적하여 복원한다. 53
54
### 10.3. 서브에이전트 생명주기 이벤트 55
56
#### 10.3.1. `agent.subagent.started` (Client → Master) 57
58
서브에이전트가 시작되었음을 통보한다. `tool.invoke.request`(`subagent` 포함) 발송 직후에 발송해야 한다 (MUST). 59
60
```json 61
{ 62
"type": "agent.subagent.started", 63
"payload": { 64
"parent_invocation_id": "String (필수, 대응하는 tool.invoke.request의 invocation_id)", 65
"agent_type": "String (선택, 서브에이전트 유형)", 66
"description": "String (선택, 서브에이전트 임무 설명)", 67
"depth": "Number (필수, 중첩 깊이. 1 = 서브에이전트, 2 = 서브의 서브에이전트)" 68
} 69
} 70
``` 71
72
#### 10.3.2. `agent.subagent.ended` (Client → Master) 73
74
서브에이전트가 종료되었음을 통보한다. `tool.invoke.result`(output_type: `agent_result`) 발송 직전에 발송해야 한다 (MUST). 75
76
```json 77
{ 78
"type": "agent.subagent.ended", 79
"payload": { 80
"parent_invocation_id": "String (필수)", 81
"status": "String (필수, 'completed' | 'failed' | 'cancelled')", 82
"duration_ms": "Number (선택, 서브에이전트 실행 시간 밀리초)" 83
} 84
} 85
``` 86
87
**이벤트 순서:** 88
89
``` 90
tool.invoke.request (subagent 포함) 91
→ agent.subagent.started 92
→ 서브에이전트 내부 메시지들 (subagent_context 포함) 93
→ agent.subagent.ended 94
→ tool.invoke.result (output_type: "agent_result") 95
``` 96
97
### 10.4. 서브에이전트 출력 98
99
서브에이전트의 중간 출력은 `tool.invoke.stream`으로 중계되거나, `subagent_context`가 포함된 개별 DPS 프레임(`agent.text.delta`, `tool.invoke.request` 등)으로 마스터에 직접 노출될 수 있다. 100
101
완료 시 `tool.invoke.result`(output_type: `agent_result`)로 최종 결과가 보고된다. 102
103
### 10.5. 사용량 보고 규칙 104
105
서브에이전트 내에서 발생하는 `session.usage`는 `subagent_context`를 포함하여 독립적으로 보고해야 한다 (MUST). 서브에이전트의 사용량을 메인 에이전트의 누적 사용량에 합산할지 여부는 마스터 또는 엣지 노드가 결정한다. 본 규격은 합산 정책을 규정하지 않는다. 106
107
### 10.6. 능력 협상 108
109
`session.capabilities`(§12.2.1)의 `features` 객체에 서브에이전트 관련 능력을 고지한다: 110
111
| 필드 | 설명 | 112
| ------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------- | 113
| `subagent` | 서브에이전트 도구 호출 지원 여부 (기존) | 114
| `subagent_context` | `metadata.subagent_context` 필드 제공 여부. `true`이면 서브에이전트 메시지에 컨텍스트를 포함한다. `false`이거나 미고지이면 모든 메시지를 플랫하게 처리한다. | 115
116
`subagent_context: false`인 클라이언트의 메시지에 대해 마스터 및 CRS는 그룹핑을 시도해서는 안 된다 (MUST NOT). 117
118
### 10.7. 서브에이전트 미지원 폴백 119
120
서브에이전트를 지원하지 않는 에이전트(프로세스 기반 어댑터 등)의 경우: 121
122
- `subagent_context`를 생략하거나 `null`로 설정해야 한다 (MUST). 123
- 모든 메시지는 메인 에이전트의 메시지로 취급된다. 124
- `output_type: "agent_result"`인 `tool.invoke.result`는 `subagent_context` 없이도 서브에이전트 결과임을 암시하지만, 내부 메시지 그룹핑은 불가능하다. 125
44 126
--- 수정됨
12. 명령어 및 능력 고지 (Command & Capability Negotiation)
추가된 줄 2 삭제된 줄 4
1 1
## 12. 명령어 및 능력 고지 (Command & Capability Negotiation) 2 2
3 3
### 12.1. 명령어 목록 조회 4 4
5 5
에이전트가 지원하는 슬래시 명령어 목록을 마스터에 고지한다. 6 6
7
### 12.1.1. `agent.commands.publish` (Client → Master) 7
#### 12.1.1. `agent.commands.publish` (Client → Master) 8 8
9 9
```json 10 10
{ 11 11
"type": "agent.commands.publish", 12 12
"payload": { 13 13
"commands": [ 14 14
{ 15 15
"name": "String (필수, 슬래시 명령명. 예: 'compact', 'cost', 'clear')", 16 16
"description": "String (필수, 명령 설명)", 17 17
"parameters": [ 18 18
{ 19 19
"name": "String (필수)", 20 20
"type": "String (필수, 'string' | 'number' | 'boolean')", 21 21
"required": "Boolean (필수)", 22 22
"description": "String (선택)" 23 23
} 24 24
], 25 25
"category": "String (선택, 'session' | 'display' | 'config' | 'navigation')" 26 26
} 27 27
] 28 28
} 29 29
} 30 30
``` 31 31
32 32
### 12.2. 능력 협상 33 33
34 34
세션 시작 직후, 양측은 `session.capabilities` 이벤트를 교환하여 지원하는 기능 목록을 협상한다. 35 35
36
### 12.2.1. `session.capabilities` (양방향) 36
#### 12.2.1. `session.capabilities` (양방향) 37 37
38 38
```json 39 39
{ 40 40
"type": "session.capabilities", 41 41
"payload": { 42 42
"protocol_version": "String (필수, 'rawp-dps-1.0')", 43 43
"supported_namespaces": ["String (필수, 지원하는 네임스페이스 목록)"], 44 44
"features": { 45 45
"thinking_stream": "Boolean (선택, 사고 스트리밍 지원)", 46 46
"tool_streaming": "Boolean (선택, 도구 출력 스트리밍 지원)", 47 47
"structured_output": "Boolean (선택, 구조화 출력 지원)", 48 48
"subagent": "Boolean (선택, 서브에이전트 지원)", 49 49
"context_compaction": "Boolean (선택, 컨텍스트 압축 지원)", 50 50
"plan_mode": "Boolean (선택, 계획 모드 지원)", 51 51
"file_reference": "Boolean (선택, 파일 참조 토큰 해석 지원 여부)", 52 52
"file_search_fuzzy": "Boolean (선택, 퍼지 파일 검색 지원 여부)" 53 53
}, 54 54
"max_frame_size": "Number (선택, 최대 단일 프레임 바이트)", 55 55
"extensions": "Object (선택, 벤더 확장 기능)" 56 56
} 57 57
} 58 58
``` 59 59
60
> **변경 사항**: `features`에 `file_reference`와 `file_search_fuzzy`가 추가되었다 (§16.5 참조). 61
62 60
수신 측은 상대방이 고지하지 않은 `features`의 이벤트를 발송해서는 안 된다 (MUST NOT). 예를 들어 마스터가 `thinking_stream: false`이면, 클라이언트는 `agent.thinking.delta`를 발송하지 않아야 한다. 63 61
64 62
**파일 참조 관련 능력 기반 필터링 규칙 (MUST)**: 65 63
66 64
- 마스터는 클라이언트가 `file_search_fuzzy: true`를 고지하지 않은 경우, `control.file.search`를 발송해서는 안 된다 (MUST NOT). 67 65
- `file_reference: false`이거나 미고지인 클라이언트에게 `file_references`가 포함된 `control.prompt.request`가 전달되는 경우, 클라이언트는 §16.2.2의 파싱 순서에 따라 토큰을 추출한 뒤 `display_path`를 리터럴 텍스트로 치환하여 에이전트에 전달한다 (MUST). 68 66
69 67
--- 수정됨
부록 A. Claude Code 출력 유형 전수 조사 및 매핑
추가된 줄 2 삭제된 줄 20
1 1
## 부록 A. Claude Code 출력 유형 전수 조사 및 매핑 2 2
3 3
본 규격 설계의 근거가 된 Claude Code의 전체 출력 유형과 RAWP-DPS 1.0 이벤트 매핑 표: 4 4
5 5
### A.1. 핵심 도구 출력 6 6
7 7
| Claude Code 도구 | 입력 요약 | 출력 형태 | RAWP-DPS 매핑 | output_type | 8 8
| ---------------- | --------------------------------------- | -------------------------- | ------------------------------------------------------------------- | ----------------------- | 9 9
| **Bash** | `command`, `description` | stdout/stderr 텍스트 | `tool.invoke.request` → `tool.invoke.stream` → `tool.invoke.result` | `text` | 10 10
| **Read** | `file_path`, `offset`, `limit` | 파일 내용 (라인 번호 포함) | `tool.invoke.result` | `file_content` | 11 11
| **Write** | `file_path`, `content` | 성공/실패 | `tool.invoke.result` | `empty` | 12 12
| **Edit** | `file_path`, `old_string`, `new_string` | 변경된 코드 + 컨텍스트 | `tool.invoke.result` | `diff` | 13 13
| **MultiEdit** | `file_path`, `edits[]` | 복수 변경사항 | `tool.invoke.result` | `diff` | 14 14
| **Glob** | `pattern`, `path` | 매칭 파일 경로 목록 | `tool.invoke.result` | `file_list` | 15 15
| **Grep** | `pattern`, `path`, `include` | 매칭 라인/파일 목록 | `tool.invoke.result` | `text` 또는 `file_list` | 16 16
| **LS** | `path` | 디렉토리 내용 | `tool.invoke.result` | `file_list` | 17 17
18 18
### A.2. 확장 도구 출력 19 19
20 20
| Claude Code 도구 | RAWP-DPS 매핑 | output_type | 21 21
| ------------------- | ------------------------------------------------------------------- | -------------------- | 22 22
| **WebFetch** | `tool.invoke.result` | `web_content` | 23 23
| **WebSearch** | `tool.invoke.result` | `web_content` | 24 24
| **NotebookRead** | `tool.invoke.result` | `file_content` | 25 25
| **NotebookEdit** | `tool.invoke.result` | `empty` 또는 `diff` | 26 26
| **TodoRead** | `tool.invoke.result` | `structured` (§8.2) | 27 27
| **TodoWrite** | `tool.invoke.result` | `structured` (§8.2) | 28 28
| **Task (Subagent)** | `tool.invoke.request` → `tool.invoke.stream` → `tool.invoke.result` | `agent_result` (§10) | 29 29
30 30
### A.3. 비도구 출력 31 31
32 32
| Claude Code 출력 | RAWP-DPS 매핑 | 33 33
| -------------------- | ------------------------------------------------------ | 34 34
| 텍스트 응답 스트리밍 | `agent.text.delta` → `agent.text.done` | 35 35
| Extended Thinking | `agent.thinking.delta` → `agent.thinking.done` | 36 36
| 도구 실행 권한 요청 | `agent.interaction.request` (type: `PERMISSION`) | 37 37
| 컨텍스트 자동 압축 | `session.compacted` (trigger: `auto`) | 38 38
| `/compact` 수동 압축 | `control.session.compact` → `session.compacted` | 39 39
| 토큰/비용 보고 | `session.usage` | 40 40
| Plan 모드 진입/이탈 | `control.mode.switch` (mode: `plan` / `default`) | 41 41
| 계획 문서 출력 | `agent.text.delta` (metadata.content_role: `plan`) | 42 42
| 슬래시 명령 입력 | `control.prompt.request` (input_type: `slash_command`) | 43 43
| 에러 보고 | `agent.error` | 44 44
| 세션 종료 | `session.turn.end` + RAWP 1.0 §5.2 | 45 45
46
### A.4. RAWP-1.0-Legacy 미지원 항목 47
48
기존 RAWP-1.0-Legacy에서 지원하지 못했으나 본 규격에서 해결된 항목: 46
### A.4. RAWP-1.0-Legacy (지원 종료) 49 47
50
| 항목 | RAWP-1.0-Legacy | RAWP-DPS 1.0 | 51
| ------------------------- | --------------- | -------------------------------------------------------- | 52
| 도구 호출/결과 프레이밍 | ❌ 미지원 | ✅ `tool.invoke.*` | 53
| 계획 문서 전달 | ❌ 미지원 | ✅ `metadata.content_role: plan` | 54
| TODO/태스크 관리 | ❌ 미지원 | ✅ §8 + `tool.invoke.result` | 55
| 컨텍스트 압축 보고 | ❌ 미지원 | ✅ `session.compacted` | 56
| 명령어 목록 조회 | ❌ 미지원 | ✅ `agent.commands.publish` | 57
| 사고 스트리밍 | ❌ 미지원 | ✅ `agent.thinking.*` | 58
| 서브에이전트 위임 | ❌ 미지원 | ✅ §10 | 59
| 구조화 출력 | ❌ 미지원 | ✅ §11 | 60
| 도구 출력 실시간 스트리밍 | ❌ 미지원 | ✅ `tool.invoke.stream` | 61
| 능력 협상 | ❌ 미지원 | ✅ `session.capabilities` | 62
| 병렬 도구 호출 그룹화 | ❌ 미지원 | ✅ `parallel_group_id` | 63
| Turn 라이프사이클 | ❌ 미지원 | ✅ `session.turn.*` | 64
| 프롬프트 취소 | ❌ 미지원 | ✅ `control.prompt.cancel` | 65
| 동작 모드 전환 | ❌ 미지원 | ✅ `control.mode.switch` | 66
| 파일 참조 검색 | ❌ 미지원 | ✅ §16 `control.file.search` / `session.file.candidates` | 48
RAWP-1.0-Legacy와의 비교 항목은 지원 종료로 더 이상 유지하지 않는다. 67 49
68 50
--- 수정됨
부록 B. 이벤트 타입 전체 목록 (Quick Reference)
추가된 줄 4 삭제된 줄 0
1 1
## 부록 B. 이벤트 타입 전체 목록 (Quick Reference) 2 2
3 3
| 이벤트 타입 | 방향 | §참조 | 4 4
| ------------------------------ | ------ | ------ | 5 5
| `agent.text.delta` | C→M | 4.1.1 | 6 6
| `agent.text.done` | C→M | 4.1.2 | 7 7
| `agent.thinking.delta` | C→M | 4.2.1 | 8 8
| `agent.thinking.done` | C→M | 4.2.2 | 9 9
| `agent.interaction.request` | C→M | 4.3.1 | 10 10
| `agent.error` | C→M | 4.4.1 | 11 11
| `agent.file.transfer` | 양방향 | 4.5.1 | 12 12
| `agent.state.changed` | C→M | 4.6.1 | 13 13
| `agent.commands.publish` | C→M | 12.1.1 | 14
| `agent.subagent.started` | C→M | 10.3.1 | 15
| `agent.subagent.ended` | C→M | 10.3.2 | 14 16
| `tool.invoke.request` | C→M | 5.2.1 | 15 17
| `tool.invoke.result` | C→M | 5.3.1 | 16 18
| `tool.invoke.stream` | C→M | 5.3.2 | 17 19
| `tool.catalog.publish` | C→M | 5.4.1 | 18 20
| `control.prompt.request` | M→C | 6.1.1 | 19 21
| `control.prompt.cancel` | M→C | 6.1.2 | 20 22
| `control.interaction.response` | M→C | 6.2.1 | 21 23
| `control.interaction.timeout` | M→C | 6.2.2 | 22 24
| `control.session.compact` | M→C | 6.3.1 | 23 25
| `control.mode.switch` | M→C | 6.3.2 | 24 26
| `control.file.search` | M→C | 16.1.1 | 25 27
| `control.file.search.cancel` | M→C | 16.1.3 | 26 28
| `session.history` | C→M | 7.1.1 | 27 29
| `session.compacted` | C→M | 7.2.1 | 28 30
| `session.usage` | 양방향 | 7.3.1 | 29 31
| `session.error` | 양방향 | 7.4.1 | 30 32
| `session.turn.start` | C→M | 7.5.1 | 31 33
| `session.turn.end` | C→M | 7.5.2 | 32 34
| `session.capabilities` | 양방향 | 12.2.1 | 33 35
| `session.file.candidates` | C→M | 16.1.2 | 36
| `session.renamed` | 양방향 | 7.6.1 | 37
| `session.deleted` | 양방향 | 7.7.1 | 추가됨
14. 하위 호환성 (Backward Compatibility)
추가된 줄 15 삭제된 줄 0
1
## 14. 하위 호환성 (Backward Compatibility) 2
3
### 14.1. RAWP-1.0-Legacy (지원 종료) 4
5
RAWP-1.0-Legacy와의 하위 호환성 지원은 종료되었다. 모든 구현은 본 규격(`rawp-dps-1.0`)만을 사용해야 한다 (MUST). 6
7
### 14.2. 정방향 호환성 규칙 8
9
1. **Unknown Type 무시**: 수신자는 알 수 없는 `type`을 가진 프레임을 무시해야 한다 (MUST). 에러를 발생시키거나 연결을 종료해서는 안 된다 (MUST NOT). 10
2. **Unknown Field 무시**: `payload` 내 알 수 없는 키는 무시해야 한다 (MUST). 11
3. **Capabilities 기반 필터링**: `session.capabilities`에서 고지하지 않은 기능의 이벤트는 발송하지 않아야 한다 (MUST NOT). 12
4. **버전 필드**: Envelope의 `v` 필드로 구조적 비호환을 감지한다. `v` 값이 지원 범위 밖이면 프레임을 무시하고 `session.error`로 응답한다. 13
5. **확장 네임스페이스**: `x-{vendor}.*` 접두 이벤트는 자유롭게 추가 가능하며, 수신자는 이를 무시할 수 있다. 14
15
--- 추가됨
17. 에이전트 어댑터 프로토콜 (Agent Adapter Protocol)
추가된 줄 72 삭제된 줄 0
1
## 17. 에이전트 어댑터 프로토콜 (Agent Adapter Protocol) 2
3
에이전트의 실행 방식에 따라 DPS 이벤트의 생성 및 전달 방식이 달라진다. 본 절은 어댑터 유형별로 DPS 프레임과의 계약을 정의한다. 어댑터의 내부 동작 방식은 규정하지 않으며, DPS 프레임 수준의 프로토콜 계약만을 정의한다. 4
5
### 17.1. 어댑터 유형 분류 6
7
에이전트 메타데이터(RAWP 1.0.2 §4.3)의 `adapter_type` 필드로 어댑터 유형을 구분한다: 8
9
| 유형 | `adapter_type` | 설명 | 10
| ------------- | -------------- | ----------------------------------------------------------------------- | 11
| 프로세스 기반 | `process` | 별도 자식 프로세스(child process)로 에이전트를 실행. stdio를 통해 통신. | 12
| SDK 기반 | `sdk` | 클라이언트와 동일 프로세스 내에서 SDK API를 통해 에이전트를 실행. | 13
14
### 17.2. 프로세스 어댑터 프로토콜 계약 15
16
#### 17.2.1. 환경 변수 17
18
프로세스 기반 에이전트 생성 시 다음 환경 변수를 주입해야 한다 (MUST): 19
20
| 환경 변수 | 값 | 설명 | 21
| --------------------- | ----------------------- | ----------------------------------------- | 22
| `RAWP_SESSION_ID` | 세션 식별자 (UUID v4) | 에이전트가 자신의 세션을 식별하기 위한 값 | 23
| `RAWP_WORKSPACE_PATH` | 작업 디렉토리 절대 경로 | 에이전트의 파일 시스템 작업 기준 경로 | 24
| `RAWP_DPS_VERSION` | `rawp-dps-1.0` | 사용 중인 DPS 프로토콜 버전 | 25
26
#### 17.2.2. Exit Code → DPS 이벤트 매핑 27
28
에이전트 프로세스 종료 시 exit code에 따라 다음 DPS 이벤트를 발송해야 한다 (MUST): 29
30
| Exit Code | 해석 | DPS 이벤트 | 31
| --------- | -------------------- | -------------------------------------------------------------------------------------------------------------- | 32
| `0` | 정상 종료 | `session.turn.end` (stop_reason: `"end_turn"`) | 33
| `1`–`127` | 에러 종료 | `agent.error` (severity: `"fatal"`) → `session.turn.end` (stop_reason: `"error"`) | 34
| `128+N` | 시그널 N에 의한 종료 | `agent.error` (severity: `"fatal"`, error_code: `"SIGNAL_EXIT"`) → `session.turn.end` (stop_reason: `"error"`) | 35
36
**예외**: `single_turn_process: true`(§18) 어댑터의 exit code 0은 Turn 완료이며 세션 종료가 아니다. §18.2 참조. 37
38
#### 17.2.3. 종료 시그널 순서 39
40
RAWP 1.0.2 §5.2의 세션 명시적 종료 시, 프로세스 기반 에이전트에 대한 종료 시그널 순서: 41
42
1. `SIGTERM` 전송. 43
2. 유예 시간(5초) 내 프로세스가 종료되지 않으면 `SIGKILL` 전송. 44
3. 프로세스 종료 확인 후 WSS 정리 프레임 발송(§7.5.3). 45
46
### 17.3. SDK 어댑터 프로토콜 계약 47
48
#### 17.3.1. 도구 승인 콜백 브릿지 49
50
SDK가 도구 실행 전 사용자 승인을 요구하는 경우, 다음의 DPS 이벤트 시퀀스를 따른다: 51
52
``` 53
SDK 콜백 발생 54
→ Client: agent.interaction.request (interaction_type: "PERMISSION") 발송 55
→ Master: control.interaction.response 또는 control.interaction.timeout 발송 56
→ Client: SDK 콜백에 승인/거부 결과 전달 57
``` 58
59
- `control.interaction.timeout` 수신 시 SDK 콜백에 거부를 전달해야 한다 (MUST). 60
- `agent.interaction.request`의 `context.tool_name`에 승인 대상 도구명을 포함해야 한다 (MUST). 61
62
#### 17.3.2. 동시 프롬프트 방지 63
64
하나의 세션에서 동시에 진행할 수 있는 에이전트 쿼리(프롬프트 처리)는 최대 1개이다 (MUST). 에이전트가 현재 Turn을 처리 중인 상태(`session.turn.start` 발송 후 `session.turn.end` 발송 전)에서 새 `control.prompt.request`를 수신하면, 클라이언트는 `session.error` (error_code: `"PROMPT_IN_PROGRESS"`, fatal: `false`)를 반환하고 해당 프롬프트를 거부해야 한다 (MUST). 65
66
### 17.4. 공통 규칙 67
68
- `session.capabilities` 교환 시 클라이언트는 현재 세션의 에이전트 어댑터 유형을 마스터에 보고해야 한다 (SHOULD). `features` 객체에 `adapter_type` 필드(`"process"` 또는 `"sdk"`)를 포함한다. 69
- `context_compaction` 미지원 어댑터가 `control.session.compact`를 수신하면 §6.3.1의 미지원 규칙을 따른다. 70
- `plan_mode` 미지원 어댑터가 `control.mode.switch`를 수신하면 §6.3.2의 미지원 규칙을 따른다. 71
72
--- 추가됨
18. 단일 턴 프로세스 재생성 (Single-Turn Process Respawn)
추가된 줄 47 삭제된 줄 0
1
## 18. 단일 턴 프로세스 재생성 (Single-Turn Process Respawn) 2
3
일부 프로세스 기반 에이전트는 하나의 프롬프트를 처리한 후 프로세스가 종료된다. 다음 프롬프트 시 이전 세션 컨텍스트를 유지하면서 새 프로세스를 생성하는 "재생성(respawn)" 패턴을 정의한다. 4
5
### 18.1. 어댑터 플래그 6
7
에이전트 메타데이터(RAWP 1.0.2 §4.3)에 다음 필드를 포함한다: 8
9
| 필드 | 타입 | 설명 | 10
| --------------------- | ------- | ------------------------------------------------------------------------------------------------------------------- | 11
| `single_turn_process` | Boolean | `true`이면 프로세스가 Turn 완료 후 exit code 0으로 종료되는 것이 정상 동작이다. | 12
| `resume_flag` | String | 재생성 시 이전 세션 컨텍스트를 전달하기 위한 CLI 플래그 템플릿. `{session_id}` 플레이스홀더를 실제 값으로 치환한다. | 13
14
### 18.2. Exit Code별 동작 15
16
`single_turn_process: true`인 에이전트의 exit code 해석: 17
18
| Exit Code | 동작 | 세션 상태 | 19
| --------- | -------------------------------------------- | ------------------------------------------ | 20
| `0` | 정상 Turn 완료. 프로세스 종료는 예상된 동작. | `idle` (다음 프롬프트 대기, 프로세스 없음) | 21
| `1`–`127` | 에러 종료. §17.2.2와 동일. | `TERMINATED` | 22
| `128+N` | 시그널 종료. §17.2.2와 동일. | `TERMINATED` | 23
24
exit code 0에서의 핵심 차이: 일반 프로세스 어댑터(§17.2.2)에서는 exit code 0이 세션 종료를 의미하지만, `single_turn_process: true`에서는 Turn 완료만을 의미하며 세션은 유지된다. 25
26
### 18.3. 세션 ID 전달 27
28
재생성 시 이전 세션 컨텍스트를 전달하기 위해 `resume_flag` 템플릿을 사용한다: 29
30
- 에이전트 프로세스가 첫 Turn 실행 중 내부 세션 ID를 보고하면(예: `session.capabilities`의 `extensions.agent_session_id`), 클라이언트는 이를 보존해야 한다 (MUST). 31
- 재생성 시 보존된 세션 ID를 `resume_flag` 템플릿의 `{session_id}` 플레이스홀더에 치환하여 프로세스 인자에 포함한다. 32
- 예: `resume_flag: "--resume {session_id}"` → 실제 실행: `agent-cli --resume abc-123-def` 33
34
### 18.4. 상태 전이 35
36
``` 37
[프롬프트 수신] → RUNNING (프로세스 생성, 에이전트 실행) 38
→ Turn 완료 (exit 0) → idle (프로세스 종료, 다음 프롬프트 대기) 39
→ [다음 프롬프트 수신] → RUNNING (프로세스 재생성, resume_flag 포함) 40
→ ... 41
``` 42
43
- `idle` 상태에서 프로세스는 존재하지 않는다. 이것이 일반 프로세스 어댑터와의 핵심 차이다. 44
- `idle` 상태에서 `DELETE /v1/session/{session_id}`(RAWP 1.0.2 §5.2)를 수신하면 종료할 프로세스가 없으므로 즉시 `TERMINATED`로 전이한다. 45
- `RUNNING` 상태에서의 세션 종료는 §17.2.3의 종료 시그널 순서를 따른다. 46
47
--- 삭제됨
14. 하위 호환성 가이드라인
추가된 줄 0 삭제된 줄 28
1
## 14. 하위 호환성 가이드라인 2
3
### 14.1. RAWP-1.0-Legacy 브릿지 4
5
RAWP-1.0-Legacy만 지원하는 클라이언트/서버와 통신할 때, 중간 프록시(Gateway)가 타입을 변환할 수 있다. 변환 규칙: 6
7
| RAWP-DPS 1.0 수신 | RAWP-1.0-Legacy 변환 | 비고 | 8
| --------------------------------------------- | ----------------------------------------------------------------- | ------------------------------------------ | 9
| `agent.text.delta` + `agent.text.done` | `agent_text_stream` (is_final 플래그로 통합) | `agent.text.done` 수신 시 `is_final: true` | 10
| `tool.invoke.request` + `tool.invoke.result` | 변환 불가, 텍스트로 직렬화 | Legacy는 도구 이벤트 미지원 | 11
| `agent.thinking.*` | 무시(drop) | Legacy는 사고 스트림 미지원 | 12
| `session.compacted` | 무시(drop) | Legacy는 압축 보고 미지원 | 13
| `session.usage` | `agent_usage_metrics` | 필드 매핑 | 14
| `session.history` | `agent_session_history` | `frames` → `history_content`로 직렬화 | 15
| `control.file.search` | 변환 불가, 무시(drop) | Legacy는 파일 검색 미지원 | 16
| `session.file.candidates` | 변환 불가, 무시(drop) | Legacy는 파일 검색 미지원 | 17
| `control.file.search.cancel` | 변환 불가, 무시(drop) | Legacy는 파일 검색 미지원 | 18
| `file_references` in `control.prompt.request` | 토큰을 `display_path` 리터럴로 치환, §16.2.2 이스케이프 해제 적용 | 텍스트 폴백 | 19
20
### 14.2. 정방향 호환성 규칙 21
22
1. **Unknown Type 무시**: 수신자는 알 수 없는 `type`을 가진 프레임을 무시해야 한다 (MUST). 에러를 발생시키거나 연결을 종료해서는 안 된다 (MUST NOT). 23
2. **Unknown Field 무시**: `payload` 내 알 수 없는 키는 무시해야 한다 (MUST). 24
3. **Capabilities 기반 필터링**: `session.capabilities`에서 고지하지 않은 기능의 이벤트는 발송하지 않아야 한다 (MUST NOT). 25
4. **버전 필드**: Envelope의 `v` 필드로 구조적 비호환을 감지한다. `v` 값이 지원 범위 밖이면 프레임을 무시하고 `session.error`로 응답한다. 26
5. **확장 네임스페이스**: `x-{vendor}.*` 접두 이벤트는 자유롭게 추가 가능하며, 수신자는 이를 무시할 수 있다. 27
28
---