## 2026-05-07 — 세션 터미널 클립보드 페이스트 수정: 하이재킹 방지 + 멀티라인 Bracketed Paste

### 배경

EXE 빌드 사용자가 세션 터미널에서 복사 붙여넣기가 빠지거나 일부만 들어오는 현상을 보고했다. 두 가지 독립적인 결함이 겹쳐 있었다 — (1) `pasteFromClipboard`가 진입 시점에 OS 클립보드의 파일 경로를 먼저 조회해서, Windows Explorer에서 파일을 복사한 상태이거나 Screenshots 폴리필 창을 사용한 뒤라면 텍스트 대신 경로가 붙여넣어지는 하이재킹. (2) `navigator.clipboard.read()`가 HTTP/LAN-IP 출처에서 reject될 때 같은 `try/catch` 블록 안의 `readText()` 폴백까지 함께 삼켜서 페이스트가 무음 소실. 별도로, 멀티라인 텍스트를 붙여넣으면 임베드된 `\n`이 개별 Enter로 인식되어 내용이 뒤섞이는 문제도 있었다 — modern CLI TUI(Claude / Gemini / Codex Ink)는 DEC bracketed paste 마커 없이는 멀티라인 페이로드를 페이스트로 인식하지 않기 때문.

### 주요 변경

#### 1. 페이스트 폴백 체인 재정렬 + 독립 try/catch 분리 (`541ec4b`)

이미지 MIME → 텍스트 → OS 파일경로 순서로 폴백 체인을 재정렬해서 텍스트 페이스트가 절대 파일 경로로 하이재킹되지 않도록 수정. `clipboard.read()`와 `clipboard.readText()`를 별도 `try/catch` 블록으로 분리해서 read() reject가 readText()까지 닫지 않게 함.

- **`src/client/src/components/SessionTerminal.tsx`**:
  - `pasteFromClipboard` 폴백 순서: (1) `clipboard.read()` 이미지 MIME — reject 시 fall-through, (2) `clipboard.readText()` 텍스트, (3) 브라우저 클립보드가 완전히 비어 있을 때만 `getClipboardImagePath()` OS 파일경로 조회
  - `useToast` + `ToastContainer` 연결 — 진짜 빈 클립보드 소실 시 사용자에게 경고 toast 노출 (디버그 추적용)
  - `[paste]` / `[paste-fallback]` 분기마다 `console.debug` 추가

#### 2. 멀티라인 페이스트 Bracketed Paste 래핑 (`a1b5b7d`)

텍스트에 `\n`이 포함될 때 DEC bracketed paste 시퀀스(`\x1b[200~…\x1b[201~`)로 래핑. 단일 라인은 기존 raw 전송 유지(bracketed paste 미지원 TUI에 이스케이프 시퀀스가 표시되는 부작용 방지).

- **`src/client/src/components/SessionTerminal.tsx`**:
  - 모듈 스코프 `wrapBracketedPaste(text)` 헬퍼 추가 — `\n` 존재 시에만 래핑
  - `pasteFromClipboard`의 `readText` 브랜치와 `setupDesktopInput`의 `handlePaste` 컨테이너 폴백 양쪽에 헬퍼 적용
  - `[paste]` / `[paste-fallback]` 로그에 `multiline=` 필드 추가

### 수정된 주요 파일

| 파일 | 변경 내용 |
|------|----------|
| `src/client/src/components/SessionTerminal.tsx` | 페이스트 폴백 체인 재정렬 + read()/readText() 독립 try/catch + wrapBracketedPaste 헬퍼 + useToast 연결 |

### 아키텍처 결정

1. **텍스트가 이미지/경로보다 우선**: 사용자가 텍스트를 복사했을 때 OS 클립보드에 파일 경로가 남아 있어 하이재킹되는 건 항상 잘못된 동작. 폴백 체인에서 OS 경로 조회는 브라우저 클립보드가 완전히 비어 있을 때의 최후 수단으로만 사용.
2. **`clipboard.read()` 실패는 침묵 폴스루**: HTTP/LAN-IP 출처에서 read()가 SECURITY_ERROR로 reject되는 건 예상된 환경 제약이지 버그가 아님. readText()는 더 관대한 API라 같은 출처에서도 성공할 수 있으므로 두 경로를 독립 try/catch로 분리해 짧게 우회.
3. **Bracketed paste는 `\n` 있을 때만**: 단일 라인 페이스트는 기존에 정상 동작했고 bracketed paste 미지원 TUI에 이스케이프 시퀀스를 노출하는 부작용을 피하기 위해 멀티라인 조건을 걸었다. Claude / Gemini / Codex Ink는 모두 bracketed paste를 지원하므로 이 래핑이 그들 사용 케이스를 커버.
