Skip to content

메신저 게이트웨이 ​

게이트웨이는 채팅 플랫폼을 세션 또는 이름 있는 에이전트에 연결합니다. 그 대화방의 메시지는 프롬프트가 되고, 에이전트의 답은 다시 메시지로 돌아오며, 승인 요청은 버튼으로 도착합니다. v0.1에서 완전히 지원되는 플랫폼은 Telegram입니다. Discord와 Slack은 같은 인터페이스를 구현하고 있고 전달은 되지만, 실전 검증은 상대적으로 덜 쌓여 있습니다.

먼저 자격 증명부터 ​

bash
snowpea setup --gateway telegram --token 123456:ABC-your-bot-token

토큰은 $SNOWPEA_HOME/credentials.json에 0600 권한으로 들어갑니다. 대신 환경 변수로 export한 뒤 바인딩할 때 그 이름을 참조해도 됩니다. 어느 쪽이든 토큰이 settings.json에 쓰이는 일은 없고, 로그 한 줄에도 나타나지 않습니다.

바인딩 ​

bash
snowpea gateway bind telegram TELEGRAM_BOT_TOKEN agent:scribe --user 987654
snowpea gateway bind telegram TELEGRAM_BOT_TOKEN new:~/src/api --channel 123456 --user 987654
snowpea gateway list --json
snowpea gateway unbind <binding-id>

세 위치 인자는 순서대로 플랫폼, 자격 증명 참조(credentials.json의 키 또는 환경 변수 이름), 대상입니다:

대상의미
agent:<name>자기 메모리 네임스페이스를 가진 이름 있는 상시 에이전트
session:<id>기존 세션
new:<workdir>첫 메시지가 오면 그 디렉터리에 세션을 새로 만듦

--channel은 바인딩을 특정 채팅 id 하나로 제한합니다. --user는 승인을 결정할 수 있는 플랫폼 사용자를 지정합니다. 둘 다 설정하세요. --user가 없으면 승인에 답할 권한을 가진 사람이 아무도 없는 상태가 되고, 게이트웨이는 누가 버튼을 눌러도 무시합니다.

봇 만들기 ​

Telegram. @BotFather에게 /newbot을 보내고 토큰을 받아 둡니다. 채팅 id는 봇에게 처음 메시지를 보냈을 때 데몬 로그에 찍히는 숫자입니다. long polling 방식이라 공개 URL도 webhook도 필요 없습니다.

Discord. 애플리케이션을 만들고 봇 유저를 추가한 뒤 message content intent를 켜고, 서버에 초대하고, 봇 토큰을 받아 둡니다. 초대할 때 bot과 applications.commands 두 스코프를 모두 주세요. 개발자 포털의 OAuth2 URL 생성기가 그 링크를 만들어 줍니다. 두 번째 스코프가 있어야 채팅 명령이 Discord의 / 목록에 나타납니다. 바인딩이 시작될 때 snowpea가 명령을 등록하므로 /sessions, /resume, /new, /projects, /status, /stop, /help, /mode, /model, /effort가 자유 입력 args 칸과 함께 목록에 뜨고, 이렇게 부른 명령의 답은 명령 자리에 그대로 돌아옵니다. 이 스코프가 없어도 깨지는 것은 없습니다. message content intent가 켜져 있으면 /sessions를 그냥 글로 쳐도 똑같이 동작합니다. 채널 id는 채널 URL의 마지막 경로 조각입니다.

Slack. Slack은 슬래시 명령을 오직 앱 매니페스트에서만 가져갑니다. 그러니 매니페스트를 출력해서 붙여 넣으세요:

bash
snowpea gateway slack-manifest

api.slack.com/apps에서 새 앱이면 Create from manifest, 이미 있는 앱이면 App Manifest를 골라 이 JSON을 붙여 넣습니다. 봇 유저, 스코프(chat:write, commands, history 계열), 메시지 이벤트, Socket Mode, 그리고 /sessions, /resume, /new, /projects, /status, /stop, /help, /mode, /model, /effort가 진짜 슬래시 명령으로 선언됩니다. 이게 보기보다 중요합니다. Slack은 /로 시작하는 메시지를 앱에 넘기기 전에 전부 가로채기 때문에, 선언되지 않은 /sessions는 Slack이 "알 수 없는 명령"이라고 답해 버리고 snowpea에는 아예 도착하지 않습니다. 선언된 명령은 Socket Mode 슬래시 명령으로 들어오고, 답은 명령을 친 채널에 그대로 올라갑니다.

그다음 앱을 설치해 봇 토큰(xoxb-…)을 받고, Socket Mode용으로 connections:write 권한의 app-level 토큰(xapp-…)도 발급받습니다. 바인딩에는 둘 다 필요합니다. 봇 토큰만 있으면 예약 메시지 전달은 되지만 수신은 되지 않습니다. 대상으로는 채널 이름이나 id를 씁니다.

대화하기 ​

봇에게 메시지를 보내면 바로 세션이 시작됩니다. 슬래시 명령은 터미널에서와 완전히 똑같이 동작하는데, 명령 레지스트리가 UI가 아니라 코어에 있기 때문입니다:

/mode
/ralph fix the failing integration test
/schedule "매일 09:00" "어제 커밋 요약" --channel telegram:123456

게이트웨이가 만든 세션은 session.list에 출처와 함께 뜨므로, 같은 데몬에 붙은 TUI에서도 그 대화방이 지금 뭘 하고 있는지 볼 수 있습니다.

대화방 명령 ​

터미널은 "내가 지금 어느 대화에 있는지, 또 뭐가 열려 있는지, 다른 데로 옮겨 달라"를 자기 창으로 답합니다. 대화방에는 창이 없으니 명령 일곱 개가 그 자리를 대신합니다. 게이트웨이가 명령 레지스트리보다 먼저 처리하며, 모델까지 가지 않습니다.

명령하는 일
/sessions최근 대화 열 개. 지금 이 방이 붙어 있는 것에 ★. 각 줄은 버튼이기도 하고, 목록 바로 뒤에 숫자만 적어도 그 줄이 선택됩니다.
/resume <번호 | id | 앞글자>이 방을 다른 세션으로 옮깁니다. 닫힌 세션은 다시 엽니다.
/new [경로 | 프로젝트 | 번호]세션을 새로 시작합니다: 직접 적은 경로, /projects의 프로젝트, 또는 인자 없이 바인딩 자신의 작업 디렉터리.
/projects알고 있는 프로젝트: IDE의 ide.projects 목록과 최근 세션들의 작업 디렉터리를 합치고 고정된 것을 앞에 둡니다. 각 줄이 거기서 세션을 시작합니다.
/status세션 id, 작업 디렉터리, 모드, 모델, effort, 지금 턴이 도는지, 대기 중인 프롬프트, 사용한 컨텍스트.
/stop도는 턴을 끊고 뒤에 쌓인 대기열도 비웁니다. TUI의 Stop과 같은 경로입니다.
/help위 명령들과, 휴대폰에서 쓸 만한 세션 명령들.

Telegram은 봇이 시작할 때 이것들을 / 메뉴로 등록하므로, 외울 필요 없이 목록에서 고르면 됩니다.

/resume, /new, /stop과 줄 버튼은 바인딩된 --user만 쓸 수 있습니다. 승인과 같은 이유입니다 — 다음 메시지가 무엇을 어디서 실행할지 정하는 일이니까요. /sessions, /projects, /status, /help는 대화방의 누구에게나 열려 있습니다.

이 방이 어느 세션에 있는지는 데몬을 재시작해도 유지됩니다. 선택은 $SNOWPEA_HOME/gateway-chats.json의 한 줄이고, 파일을 지우면 모든 방이 각자 바인딩의 원래 대상으로 돌아갑니다.

입력 중 표시와 진행 메시지 ​

턴이 도는 동안 대화방에는 플랫폼의 "입력 중…" 표시가 뜨고, 몇 초마다 갱신됩니다. 승인이나 질문이 당신의 답을 기다리는 동안에는 멈춥니다 — 당신이 고민하는 동안 에이전트는 일하고 있지 않으니까요. 턴의 첫 툴 호출에서 메시지 하나가 나가고(⏳ shell npm test), 이후의 툴 호출은 새 메시지 대신 그 메시지를 고쳐 씁니다. 끝나면 ✓ 4 tool calls · 1m 12s로, 실패하거나 중단됐으면 ✗로 정리됩니다.

메시지를 고쳐 쓸 수 없는 플랫폼에는 진행 메시지를 아예 보내지 않습니다. 고쳐 쓰기가 없으면 이 기능은 그냥 도배이기 때문입니다. Slack은 입력 중 표시도 없습니다 — 봇 토큰으로는 보낼 수 없습니다. 그래서 Slack 대화방에는 진행 메시지만 남고, 그 진행 메시지는 제자리에서 계속 고쳐 쓰입니다.

진행 메시지는 약 1초 간격으로 변경을 모아 갱신하며, 빠른 결과도 후속 갱신으로 보여줍니다. 진행 메시지에는 최근 도구 입력·결과, 서브에이전트 식별 정보와 종료 보고, 자식 도구 호출·결과도 구분해 표시합니다. 긴 출력은 제한하고 인증 정보와 민감한 결과는 가립니다. 전체 기록은 세션에 남으며, 진행 메시지는 추가 발송 대신 같은 메시지를 갱신합니다.

설정 두 개로 끌 수 있습니다:

json
{ "gateway": { "typing": false, "progress": false } }

기본값은 둘 다 true입니다. settings.gateway 안에서 플랫폼 블록과 나란히 있지만, 메신저가 아니라 스위치입니다.

무인 승인 ​

게이트웨이나 스케줄러 세션에서 올라온 승인 요청은 무인 상태이고, 타이핑으로 직접 일으킨 승인과는 다르게 동작합니다:

  • 붙어 있는 모든 클라이언트에 approval.pending 알림으로 방송되므로 TUI 승인 큐에도 뜹니다.
  • 바인딩된 대화방에 허용·거부 버튼과 함께 전송됩니다.
  • 어느 쪽이든 먼저 온 응답이 이깁니다. 나머지 모두는 결정 내용과 누가 결정했는지를 담은 approval.resolved를 받습니다.
  • 바인딩된 --user만 대화방에서 답할 수 있습니다. 다른 사람의 버튼 클릭은 무시되고 로그로 남습니다.
  • 아무 답이 없이 approvals.timeoutSec(기본 300초)이 지나면 거부됩니다.

TUI에서 타이핑으로 일으킨 승인은 그 화면에만 머물고 공유 큐에 절대 들어가지 않습니다. 이 경계가 핵심입니다 — 내 터미널의 프롬프트가 그룹 채팅으로 새어 나가지 않고, 채팅 승인을 낯선 사람이 대신 답할 수도 없습니다.

결정된 모든 건은 요청 id, 툴, 결정, 결정한 사람, 범위, 무인 여부와 함께 $SNOWPEA_HOME/logs/approvals.jsonl에 추가됩니다.

에이전트가 묻는 질문 ​

ask_user 도구는 에이전트가 당신에게 묻는 것이고, 바인딩된 대화방에는 선택지마다 버튼이 달린 메시지로 도착합니다.

text
❓ 렌더러
렌더링을 무엇으로 할까요? 여기서 고른 것이 엔진 통제력을 정합니다.
1. three.js (추천) — 빠른 시작, 엔진 통제력 낮음
2. Raw WebGL2 — 공수 큼, 완전한 통제
(번호로 답해 주세요 / reply with the number, or type your own answer)
[1. three.js (추천)] [2. Raw WebGL2] [✏️ 기타 / Other]

승인과 달리 대화방의 누구나 답할 수 있습니다. 질문은 아무 권한도 주지 않으므로 낯선 사람이 악용할 것이 없습니다. 버튼을 누르거나 번호를 그냥 적어도 됩니다 — 2, 여러 개를 고르는 질문이면 1,3. 번호가 아닌 것은 자유 입력 답으로 받으며, "기타" 버튼이 요청하는 것도 그것입니다.

ask_user 한 번에 질문이 여러 개일 때 터미널은 탭으로 보여 주지만, 대화방에는 탭이 없으므로 게이트웨이가 하나씩 올립니다 — 머리줄이 몇 번째인지 세어 주고(❓ 저장 (2/3)), 지금 질문에 답해야 다음 질문이 나옵니다. 마지막 답이 들어오면 전체가 한 번에 돌아갑니다. questions.timeoutSec(기본 600초)이 지나면 아무도 답하지 않았다고 에이전트에게 전달됩니다.

이름 있는 에이전트 ​

이름 있는 에이전트는 자기 세션, 자기 메모리 네임스페이스, 자기 채널, 자기 잡을 가진 채 데몬 재시작을 넘겨 살아남습니다. 두 이름 있는 에이전트는 서로의 메모리를 읽을 수 없으므로, 하나에는 업무용 채널을, 다른 하나에는 개인용 채널을 맡겨도 안전합니다.

bash
snowpea agents --json

/agent create "<description>"으로 하나 만들고 gateway bind ... agent:<name>으로 채널을 바인딩하면, 다음 데몬 기동 시 바인딩까지 함께 복원됩니다. 이름 있는 에이전트는 네 번째 keepalive 카운터라서, 하나라도 있는 데몬은 유휴 상태로 종료되지 않습니다.

보안 ​

게이트웨이는 원격 실행 경로입니다. 그렇게 다루세요.

  • --user를 바인딩하세요. 항상.
  • 완전히 통제하지 못하는 대화방에서 닿는 작업에는 plan 모드를 우선하세요.
  • 승인 요청은 일회용이고 요청 id 하나에 묶여 있어서, 버튼 클릭을 재전송해도 아무 일도 일어나지 않습니다.
  • 자격 증명은 0600 파일에 있고, 가능하면 OS 키체인을 씁니다.
  • approvals.jsonl을 가끔 들여다보세요. 내가 안 보는 사이에 무엇이 허용됐는지의 기록입니다.

다음 ​

백엔드 — 툴을 다른 곳에서 실행하기.

MIT licensed. The manual is generated from the product repositories — see the banner on each page.