authoring-channellisted
Install: claude install-skill relayax/relayagent
# 채널 어댑터 저작
채널은 **글루**다. 외부 이벤트를 착신으로, 회신을 외부 발화로 옮길 뿐 판단하지 않는다.
판단이 필요하면 착신하라 — 지능은 세션에 산다.
불변식: **채널의 모든 대화는 RELAY_API 를 통과한다.** 세션 이력·목록·담장·파일 무대가
전부 기판 장부라서, 장부를 우회한 대화는 기판이 이어가지도 복원하지도 못한다.
플랫폼 SDK 가 주는 "봇이 직접 답하기" 축으로 지능을 어댑터 안에 넣고 싶어질 텐데,
그 순간 대화가 장부 밖으로 나간다 — 어댑터는 운반만 한다.
## 시작 방법
1. **정본은 스키마다.** `relay.manifest.yaml` 의 `surfaces.channels` 주석이 프로세스 계약
전문이다. 이 문서는 요약이라 어긋나면 스키마가 이긴다.
2. `references/slack.ts` 를 읽는다. 계약 전 조항(게이트·slot·직렬화·dedup·봉투·파일·
발신·실패·검사)을 구현한 참조 구현이다. 복사해서 플랫폼 부분만 갈아끼우는 시작을 권한다.
3. `relay.yaml` 에 선언한다. 채널 이름은 서비스와 같은 자격 이름공간을 쓴다 —
겹치면 판정이 거부한다.
```yaml
surfaces:
channels:
- name: slack
source: channels/slack
entry: index.ts
```
4. 완료 기준: `relay validate` → 설치(conform 게이트 통과) → `relay harness-check <패키지>`
→ 실자격으로 왕복 한 번. 넷 다 지나기 전에는 완료가 아니다.
## 프로세스 계약 요약
기판 왕복의 정본은 **클라이언트 전송 계약 v1**(`docs/client-protocol.md`)이다.
구 블로킹 `POST …/chat` 은 은퇴했다 — 개설과 관찰이 분리된 아래 두 동사가 그 자리다.
기판이 entry 를 `node --experimental-strip-types` 로 스폰해 상주시킨다.
| 축 | 계약 |
|---|---|
| env | `RELAY_NAME` `RELAY_CHANNEL` `RELAY_API` `RELAY_TOKEN` + `RELAY_CRED_<이름>` (vault) |
| 착신 | ① 개설 `POST {RELAY_API}/pkg/{RELAY_NAME}/turns` body `{message, session, attachments?}` → **202** `{turn, session}` (턴 종결을 붙들지 않는다) ② 관찰 `GET …/turns/<turn>/stream` SSE |
| 관찰 | `data:` 한 줄 = 봉투 이벤트 JSON 하나. 어휘는 하네스 봉투 protocol 3 그대로 — `delta`·`tool`·`usage`·`task`·`ask`·`file`·`reply`·`error`. 종결은 `reply`/`error` 정확히 하나, **스트림의 끝은 수명주기 `{event:"t