1.0.1 변경 이력 1.0.0 기준

변경 이력

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 ---