# RAWP-CRS 1.0: Client Rendering Specification — Part 1 (§1–§3)
> **본 파일은 RAWP-CRS 1.0의 §1–§3을 포함합니다. §4–§7 및 부록은 Part 2를 참조하십시오.**
| 항목 | 값 |
| ---------------- | ------------------ |
| 상태 | Stable |
| 버전 | 1.0.2 |
| 상위 규격 | **RAWP 1.0.3** |
| 데이터 평면 규격 | **RAWP-DPS 1.0.1** |
---
## 1. 개요 (Introduction)
RAWP-CRS 1.0은 RAWP 1.0 프로토콜 기반의 대화형 에이전트 인터페이스를 구현하는 클라이언트의 시각적 렌더링 및 UX 행동 규격이다. 본 규격은 RAWP 1.0의 제어 평면 이벤트와 RAWP-DPS 1.0의 데이터 평면 이벤트를 사용자에게 시각적으로 전달하는 방법을 정의하며, 대화 메시지의 레이아웃, 콘텐츠 렌더링, 상태 표시, 애니메이션 등 클라이언트 UI 구현에 필요한 모든 시각적 요구사항을 포괄한다.
본 규격은 프론트엔드 또는 시각적 피드백(웹 클라이언트, 데스크톱 앱, 터미널, 대시보드 등)을 구현하는 모든 시스템에 적용된다.
### 1.1. 요구사항 표기 규약 (Requirements Notation)
본 문서의 "MUST", "MUST NOT", "REQUIRED", "SHOULD", "SHOULD NOT", "OPTIONAL"은 RFC 2119를 따른다.
### 1.2. 용어 정의 (Terminology)
| 용어 | 정의 |
| ---------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------- |
| User | 대화 인터페이스를 통해 에이전트와 상호작용하는 최종 사용자. |
| Agent | RAWP 세션 내에서 실행되는 작업 프로세스. 텍스트 응답, 도구 호출, 사고 과정 등 다양한 형식의 출력을 생성한다. |
| System | 프로토콜 또는 클라이언트 자체가 생성하는 메타 정보 메시지. 세션 상태 안내, 에러 표시, 중지 알림 등을 포함한다. |
| Turn | 사용자의 단일 프롬프트(`control.prompt.request`)에 대한 에이전트의 전체 응답 사이클. `session.turn.start`부터 `session.turn.end`까지의 논리적 단위. |
| 버블 (Bubble) | 메시지를 감싸는 둥근 모서리의 배경색 컨테이너. 메신저 앱에서 발신/수신 메시지를 시각적으로 구분하는 관례적 UI 요소. |
| 버블리스 (Bubble-less) | 버블 컨테이너 없이 콘텐츠 자체의 시각적 형태로 렌더링하는 방식. |
| Ephemeral | 대화 이력에 영구적으로 기록되지 않는 일시적 UI 요소. 세션 재연결이나 이력 조회 시 표시되지 않는다. |
### 1.3. 적용 범위 (Scope)
본 규격은 다음을 정의한다:
- 대화 메시지의 행위자별(User, Agent, System) 시각적 정체성 및 레이아웃 규칙
- 마크다운 콘텐츠 렌더링 컴포넌트 명세
- 실시간 스트리밍 텍스트 렌더링 및 스크롤 제어
- 사고 과정(Thinking), 도구 호출 결과, Code Diff, 태스크 목록의 시각화 규칙
- 에이전트 상태 표시 및 전환 애니메이션
- 슬래시 명령어 자동 완성 UI 및 명령어 실행 버블 렌더링
- **파일 참조 입력(`@` 트리거 퍼지 검색) 및 인라인 토큰 렌더링** (§3.4)
- 시스템 메시지의 생략 금지 원칙 및 드롭다운 패턴
- 비스트리밍 환경(Discord, Slack 등) 적응 규칙
- **파일 브라우저 UI** — 세션 생성 전 작업 디렉토리 선택을 위한 트리 뷰 및 내비게이션 (§7)
본 규격은 다음을 정의하지 않는다:
- RAWP 제어 평면의 HTTP 엔드포인트 정의 (RAWP 1.0 참조)
- WSS 데이터 프레임의 Envelope 구조 및 이벤트 타입 (RAWP-DPS 1.0 참조)
- 클라이언트-서버 간 인증 및 토큰 교환 절차 (RAWP 1.0 §3 참조)
### 1.4. 호환성 및 파싱 규약 (Forward Compatibility)
- **알 수 없는 이벤트 무시**: 클라이언트의 렌더러는 본 규격에 정의되지 않은 `agent.*`, `tool.*`, `session.*` 이벤트 타입을 수신하더라도 UI를 중단하지 않고 조용히 무시해야 한다 (MUST).
- **알 수 없는 마크다운 구문 폴백**: 렌더러가 지원하지 않는 마크다운 확장 구문을 만나면 원문 텍스트를 그대로 표시해야 한다 (MUST). 렌더링 에러를 발생시키거나 해당 텍스트를 숨겨서는 안 된다.
- **알 수 없는 `output_type` 폴백**: `tool.invoke.result`의 `output_type`이 본 규격에 정의되지 않은 값인 경우, 모노스페이스 텍스트 블록으로 폴백 렌더링해야 한다 (MUST).
---
## 2. 메시지 아키텍처 및 레이아웃 원칙 (Message Architecture)
### 2.1. 메시지 행위자 정의 및 설계 목표
대화 인터페이스는 다음 세 가지 행위자의 메시지로 구성된다:
| 행위자 | 정의 | 설계 목표 |
| ---------- | ------------------------------------------------------------------------------------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **User** | 사용자가 입력한 자연어 텍스트 또는 슬래시 명령어 | 사용자가 **1:1 채팅을 하고 있다는 자연스러운 대화 경험**을 제공한다. 메시지 형태는 친숙한 메신저 앱의 발신 메시지와 동일한 시각적 언어를 따른다. |
| **Agent** | 에이전트가 생성한 텍스트 응답, 사고 과정, 도구 호출 결과, 코드 diff, 태스크 목록 등 다양한 형식의 출력 | **다양한 형식의 콘텐츠를 복잡함 없이** 제시한다. 텍스트, 코드, diff, 파일 트리, 태스크 목록 등 이질적인 콘텐츠가 하나의 흐름에서 자연스럽게 읽히도록 한다. |
| **System** | 프로토콜 또는 클라이언트가 생성한 상태 안내(중지 알림, 에러 표시, 세션 이벤트 등) | 대화의 맥락과 상태를 **빠짐없이, 직관적으로** 전달한다. 시스템 메시지는 보조 정보이므로 대화 흐름을 방해하지 않되, 모든 내용을 사용자에게 반드시 전달해야 한다. |
### 2.2. 레이아웃 정렬 규칙
각 행위자의 메시지는 수평 정렬(alignment)로 즉시 구분 가능해야 한다 (MUST):
| 행위자 | 정렬 | 버블 | 근거 |
| ---------- | ------------- | --------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **User** | **우측 정렬** | 채팅 버블 사용 (MUST) | 메신저 앱의 관례를 따라 발신 메시지로 인지되도록 한다. 배경색이 있는 둥근 모서리의 버블로 감싸 "내가 보낸 메시지"임을 즉각적으로 전달한다. |
| **Agent** | **좌측 정렬** | 채팅 버블 미사용 (MUST NOT) | 에이전트는 텍스트, 코드 블록, diff 뷰어, 태스크 목록, 파일 트리 등 다양한 형식의 콘텐츠를 출력한다. 이들을 채팅 버블로 감싸면 콘텐츠 유형별 시각적 구분이 무너지고 중첩된 컨테이너로 인해 가시성이 저하된다. 에이전트 출력은 버블 없이 대화 영역의 전체 가용 너비를 활용하여 렌더링한다. |
| **System** | **중앙 정렬** | 채팅 버블 미사용 (MUST NOT) | 시스템 메시지는 대화의 두 참여자(User, Agent) 사이의 메타 정보이므로, 좌우 어느 쪽에도 귀속되지 않는 중립적 위치에 배치한다. 배경색 없이 연한 색상의 텍스트로 표시하여 시각적 비중을 최소화한다. |
### 2.3. 상호작용 및 애니메이션 기본 원칙
**불필요한 애니메이션 제한**: 실제 클릭 등 인터랙션이 없는 정적 요소(단순 텍스트 스트림 출력부 등)에는 불필요하게 호버(Hover) 애니메이션을 적용하지 않아야 한다 (MUST NOT).
**방해 없는 애니메이션 효과**: 버튼(상호작용 요청의 옵션 등)과 같이 실제 인터랙션이 존재하는 곳은, 반드시 사용자의 작업 흐름이나 시선을 방해하지 않는 선에서 부드러운 애니메이션 효과를 제공해야 한다 (MUST).
### 2.4. 타임스탬프 표시 규칙 (Timestamp Display)
사용자는 대화 이력에서 각 메시지가 언제 발생했는지를 확인할 수 있어야 한다 (MUST). 그러나 모든 메시지 블록에 개별 타임스탬프를 표시하면 대화 흐름이 시각적으로 복잡해지고, 특히 에이전트의 다중 출력 블록(텍스트 → 도구 호출 → 텍스트 등)이 연속되는 구간에서 타임스탬프가 콘텐츠보다 많아지는 현상이 발생한다. 따라서 타임스탬프는 **Turn 경계 및 시스템 메시지 시점**에만 표시하여, 시간 정보의 유용성과 대화 인터페이스의 간결함을 동시에 확보한다.
**표시 시점 규칙**: 타임스탬프는 다음의 경우에만 대화 이력에 표시해야 한다 (MUST):
| 표시 시점 | 위치 | 표시하는 시각 | 근거 |
| ------------------------------------- | ------------------------------------ | ---------------------------------- | ---------------------------------------------------------------------------------------------------------------- |
| **유저 메시지 발송 시** (Turn 시작) | 유저 메시지 버블의 하단 또는 상단 | `control.prompt.request` 발송 시각 | 사용자가 언제 질문/명령을 보냈는지를 기록한다. 이것이 Turn의 시작점이다. |
| **에이전트 Turn 완료 시** (Turn 종료) | 에이전트의 마지막 출력 블록 하단 | `session.turn.end` 수신 시각 | 에이전트가 언제 응답을 완료했는지를 기록한다. 유저 메시지 타임스탬프와 비교하면 응답 소요 시간을 파악할 수 있다. |
| **시스템 메시지 발생 시** | 시스템 메시지 본문 내 또는 인접 위치 | 해당 이벤트 발생 시각 | 중지, 에러, 재연결 등 시스템 이벤트의 발생 시점을 기록한다. |
**에이전트 중간 블록에는 표시하지 않음**: 단일 Turn 내에서 에이전트가 생성하는 개별 출력 블록(텍스트 스트리밍, 사고 과정, 도구 호출, 도구 결과 등)에는 개별 타임스탬프를 표시하지 않는다 (MUST NOT). 에이전트의 Turn은 하나의 연속적인 응답 흐름이며, 중간 블록마다 시각을 표시하면 대화가 로그 뷰어처럼 보여 가독성이 저해된다. Turn 시작(유저 메시지)과 Turn 종료(에이전트 마지막 출력) 두 지점의 타임스탬프만으로 응답의 시간 맥락을 충분히 파악할 수 있다.
**기본 가시성**: 타임스탬프는 기본적으로 항상 표시(always visible)하는 것이 아니라, **사용자가 원할 때 확인할 수 있는 형태**로 제공할 것을 권장한다 (SHOULD). 구체적으로 다음 중 하나의 방식을 적용한다:
- **호버/탭 노출 (권장)**: 타임스탬프를 기본적으로 숨기고, 해당 메시지 영역에 마우스를 호버하거나 모바일에서 탭하면 타임스탬프가 나타나는 방식. 대화 흐름이 가장 깔끔하게 유지된다.
- **인라인 연한 표시**: Turn 경계 시점에 연한 색상(muted color)과 작은 폰트 크기로 타임스탬프를 상시 표시하는 방식. 시각적 비중을 최소화하되 스크롤만으로 시간을 확인할 수 있다.
**날짜 구분선 (Date Separator)**: 대화가 날짜를 넘겨서 이어지는 경우, 날짜가 변경되는 지점에 중앙 정렬 날짜 구분선(예: `── 2025년 3월 11일 ──`)을 삽입하여 시간 맥락의 단절을 명확히 해야 한다 (SHOULD). 이 구분선은 시스템 메시지와 동일한 중앙 정렬, 연한 색상 스타일을 따른다.
**시각 포맷**: 타임스탬프는 사용자의 로케일과 시간대에 맞춘 현지 시각으로 표시해야 한다 (MUST). 당일 메시지는 시:분(예: `14:32`), 이전 날짜의 메시지는 날짜+시:분(예: `3/10 14:32` 또는 로케일에 따른 포맷)으로 표시할 것을 권장한다 (SHOULD).
### 2.5. 키보드 인터랙션 (Keyboard Interaction)
대화 인터페이스에서 사용자가 키보드로 수행할 수 있는 공통 인터랙션을 정의한다. 팝업(슬래시 명령어 §3.2, 파일 참조 §3.4) 내부의 키보드 내비게이션은 각 섹션에서 별도 정의한다.
#### 2.5.1. 메시지 전송
| 키 | 동작 | 조건 |
| ------------- | ----------- | ------------------------------------------------------ |
| `Enter` | 메시지 전송 | 입력창에 텍스트가 존재하고, 팝업이 활성 상태가 아닐 때 |
| `Shift+Enter` | 줄바꿈 삽입 | 항상. 메시지를 전송하지 않는다. |
`Enter`로 전송 시 입력창의 내용이 비어 있으면(공백만 포함) 전송하지 않아야 한다 (MUST NOT).
#### 2.5.2. 에이전트 응답 취소 (`Escape`)
에이전트가 응답 중(Turn 진행 중)일 때 `Escape` 키를 누르면, 클라이언트는 `control.prompt.cancel`(RAWP-DPS 1.0.1 §6.1.2)을 발송하여 현재 Turn을 취소해야 한다 (MUST).
**취소 가능 상태**: 다음 에이전트 상태에서 `Escape`가 취소를 트리거해야 한다 (MUST):
| 에이전트 상태 | `Escape` 동작 |
| ----------------------------------------------- | ---------------------------------------- |
| `thinking` (사고 중) | Turn 취소 (`control.prompt.cancel` 발송) |
| `tool_calling` (도구 실행 중) | Turn 취소 |
| 텍스트 스트리밍 중 (`agent.text.delta` 수신 중) | Turn 취소 |
**취소 불가 상태**: 다음 상태에서는 `Escape`가 취소를 트리거하지 않는다:
| 상태 | `Escape` 동작 |
| -------------------------------- | -------------------------------------- |
| `idle` (에이전트 유휴) | 동작 없음 |
| `awaiting_input` (상호작용 대기) | 상호작용 UI 닫기 (§2.5.3 참조) |
| 팝업 활성 상태 (§3.2, §3.4) | 팝업 닫기 (각 섹션의 키보드 규칙 적용) |
**취소 후 동작**: §5.2의 사용자 중지 피드백 규칙을 따른다.
#### 2.5.3. 상호작용 응답 키보드 조작
`agent.interaction.request`(RAWP-DPS 1.0.1 §4.3.1)에 의해 표시된 상호작용 UI에 대한 키보드 조작:
| `interaction_type` | 키보드 조작 |
| ------------------ | -------------------------------------------------------------------- |
| `YN` | `Y` 또는 `Enter` → 승인, `N` 또는 `Escape` → 거부 |
| `PERMISSION` | `Enter` → 승인, `Escape` → 거부 |
| `SELECT` | `↑`/`↓` → 항목 이동, `Enter` → 선택, `Escape` → 취소 |
| `MULTI_SELECT` | `↑`/`↓` → 항목 이동, `Space` → 토글, `Enter` → 확정, `Escape` → 취소 |
| `TEXT_INPUT` | `Enter` → 제출, `Escape` → 취소 |
`Escape`로 상호작용을 취소하면 `default_value`가 정의된 경우 해당 값으로 자동 응답하고, 정의되지 않은 경우 타임아웃과 동일하게 처리해야 한다 (MUST).
#### 2.5.4. 대화 이력 내비게이션
| 키 | 동작 |
| ----------------------- | -------------------------------------------------------------------------------------------------------------- |
| `↑` / `↓` | 입력창이 비어 있을 때 이전/다음 유저 메시지를 입력창에 불러오기 (MAY). 셸의 히스토리 내비게이션과 동일한 패턴. |
| `Page Up` / `Page Down` | 대화 이력 영역을 한 페이지씩 스크롤 (SHOULD) |
| `Home` | 대화 이력의 최상단으로 스크롤 (MAY) |
| `End` | 대화 이력의 최하단(최신 메시지)으로 스크롤 (MAY) |
#### 2.5.5. 입력창 포커스
대화 인터페이스가 활성 상태일 때, 팝업이나 상호작용 UI가 표시되지 않은 상태에서 사용자가 키보드 입력을 시작하면, 입력창에 자동으로 포커스가 이동해야 한다 (SHOULD). 이는 사용자가 별도의 클릭 없이 바로 타이핑을 시작할 수 있도록 하기 위함이다.
### 2.6. 대화 이력 점진적 로딩 (Conversation History Lazy Loading)
기존 대화를 다시 열 때, 전체 Turn을 한 번에 렌더링하면 Turn 수가 많은 대화에서 심각한 성능 저하가 발생한다. 이를 방지하기 위해 클라이언트는 **최신 N개 Turn만 초기 로딩**하고, 나머지는 사용자의 명시적 요청에 의해 추가 로딩하는 점진적 로딩(Lazy Loading) 방식을 적용해야 한다 (MUST).
#### 2.6.1. 초기 로딩 규칙
대화를 열 때 클라이언트는 가장 최근의 **N개 Turn**만 렌더링해야 한다 (MUST). 여기서 1 Turn은 사용자 메시지 1회와 그에 대한 에이전트의 전체 응답(텍스트, 도구 호출, 사고 과정 등)을 포함하는 논리적 단위이다.
**기본값**: N의 기본값은 **10**으로 한다 (SHOULD).
**설정 가능성**: N 값은 사용자가 클라이언트 설정에서 변경할 수 있어야 한다 (MUST). 최솟값은 1, 최댓값은 클라이언트 구현에 따르되 100 이하로 제한한다 (SHOULD).
#### 2.6.2. 이전 Turn 로딩 UI
초기 로딩 범위 밖의 이전 Turn이 존재하는 경우, 대화 이력의 최상단에 **이전 Turn 로딩 트리거**를 표시해야 한다 (MUST).
**트리거 형태**: 다음 중 하나의 방식을 사용한다:
- **명시적 버튼**: "이전 대화 불러오기" 또는 유사한 라벨의 버튼을 대화 이력 최상단에 배치한다. 클릭 시 이전 N개 Turn을 추가 로딩한다.
- **스크롤 기반 자동 로딩**: 사용자가 대화 이력을 최상단까지 스크롤하면 자동으로 이전 N개 Turn을 로딩한다. 로딩 중에는 스피너 또는 스켈레톤 UI를 표시해야 한다 (MUST).
두 방식을 함께 제공할 수 있다 (MAY).
#### 2.6.3. 로딩 동작 규칙
- **추가 로딩 단위**: 한 번의 로딩 요청에서 N개 Turn 단위로 추가 로딩한다 (SHOULD). 남은 Turn이 N개 미만이면 나머지를 모두 로딩한다.
- **스크롤 위치 유지**: 이전 Turn이 로딩되어 상단에 삽입될 때, 현재 사용자가 보고 있는 콘텐츠의 스크롤 위치가 변경되어서는 안 된다 (MUST). 새 콘텐츠는 현재 뷰포트 위쪽에 삽입되고, 스크롤 오프셋을 보정하여 사용자의 시각적 위치를 유지한다.
- **전체 로딩 완료 표시**: 모든 Turn이 로딩되면 로딩 트리거를 제거하거나 비활성화하여 더 이상 로딩할 내용이 없음을 표시해야 한다 (MUST).
- **로딩 실패**: 이전 Turn 로딩이 실패한 경우, 에러 메시지와 함께 재시도 옵션을 제공해야 한다 (MUST).
---
## 3. 유저 메시지 (User Message)
유저 메시지의 설계 목표는 **사용자가 1:1 채팅을 하고 있다는 자연스러운 경험**을 제공하는 것이다. 시각적 형태는 친숙한 메신저 앱의 발신 메시지 관례를 따른다.
### 3.1. 버블 스타일 및 레이아웃
**우측 정렬 채팅 버블**: 유저 메시지는 대화 영역의 우측에 배경색이 있는 둥근 모서리(border-radius) 버블로 렌더링해야 한다 (MUST). 버블의 최대 너비는 대화 영역 전체 폭의 70~80%로 제한하여 좌측에 여백을 확보하고, 에이전트 메시지와의 시각적 대칭을 유지한다 (SHOULD).
**텍스트 메시지**: `control.prompt.request`에서 `input_type: "text"`인 메시지는 입력된 텍스트를 버블 내에 마크다운 서식을 적용하여 표시한다.
**유저 메시지 서식 인식**: 유저 메시지 버블 내에서 다음의 텍스트 마크다운 서식을 인식하고 렌더링해야 한다 (MUST):
| 서식 | 구문 | 렌더링 |
| --------------- | -------------------------------- | ------------------------------------ |
| **볼드** | `**text**` 또는 `__text__` | `font-weight: bold` 적용 |
| **이텔릭** | `*text*` 또는 `_text_` | `font-style: italic` 적용 |
| **볼드+이텔릭** | `***text***` | 볼드와 이텔릭 동시 적용 |
| **취소선** | `~~text~~` | `text-decoration: line-through` 적용 |
| **인라인 코드** | `` `code` `` | 모노스페이스 폰트, 배경색 구분 |
| **줄바꿈** | Enter 키 (`\n`) 또는 Shift+Enter | `
` 줄바꿈 표시 |
| **링크** | `[text](url)` | 클릭 가능한 하이퍼링크 |
| **자동 링크** | URL 패턴 자동 감지 | 클릭 가능한 링크로 변환 |
| **불릿 목록** | `- item` 또는 `* item` | `