diff --git a/docs/ko/channels/discord.md b/docs/ko/channels/discord.md index 97fcfa779..08498feef 100644 --- a/docs/ko/channels/discord.md +++ b/docs/ko/channels/discord.md @@ -1,22 +1,22 @@ --- read_when: - - Discord 채널 기능 작업 중 + - Discord 채널 기능 작업 summary: Discord 봇 지원 상태, 기능 및 구성 title: Discord x-i18n: - generated_at: "2026-05-04T02:21:20Z" + generated_at: "2026-05-04T07:02:43Z" model: gpt-5.5 provider: openai - source_hash: df4e045e39f8977f779fe409abf41dad0d950c92f1230c51ff356343513df812 + source_hash: 1e00f9d9b134296ac1ca52bb4058fc62ea7a95c4d46d9478648b2ecdd448652a source_path: channels/discord.md workflow: 16 --- -공식 Discord gateway를 통해 DM과 길드 채널에서 사용할 준비가 되어 있습니다. +Discord의 공식 Gateway를 통해 DM 및 길드 채널에서 사용할 수 있습니다. - Discord DM은 기본적으로 페어링 모드입니다. + Discord DM은 기본적으로 페어링 모드로 시작합니다. 네이티브 명령 동작과 명령 카탈로그입니다. @@ -28,38 +28,38 @@ x-i18n: ## 빠른 설정 -봇이 포함된 새 애플리케이션을 만들고, 봇을 서버에 추가한 다음 OpenClaw에 페어링해야 합니다. 봇은 본인의 비공개 서버에 추가하는 것을 권장합니다. 아직 서버가 없다면 [먼저 하나 만드세요](https://support.discord.com/hc/en-us/articles/204849977-How-do-I-create-a-server) (**Create My Own > For me and my friends** 선택). +봇이 포함된 새 애플리케이션을 만들고, 봇을 서버에 추가한 다음, OpenClaw에 페어링해야 합니다. 봇은 본인의 비공개 서버에 추가하는 것을 권장합니다. 아직 서버가 없다면 [먼저 서버를 만드세요](https://support.discord.com/hc/en-us/articles/204849977-How-do-I-create-a-server) (**Create My Own > For me and my friends** 선택). - [Discord Developer Portal](https://discord.com/developers/applications)로 이동하고 **New Application**을 클릭합니다. 이름은 "OpenClaw"처럼 지정합니다. + [Discord Developer Portal](https://discord.com/developers/applications)로 이동하여 **New Application**을 클릭합니다. 이름은 "OpenClaw"처럼 지정합니다. - 사이드바에서 **Bot**을 클릭합니다. **Username**을 OpenClaw 에이전트라고 부르는 이름으로 설정합니다. + 사이드바에서 **Bot**을 클릭합니다. **Username**을 OpenClaw 에이전트를 부르는 이름으로 설정합니다. - 계속 **Bot** 페이지에서 **Privileged Gateway Intents**까지 아래로 스크롤하고 다음을 활성화합니다. + 계속 **Bot** 페이지에서 아래로 스크롤하여 **Privileged Gateway Intents**로 이동한 다음 다음을 활성화합니다. - **Message Content Intent** (필수) - **Server Members Intent** (권장, 역할 허용 목록 및 이름-ID 매칭에 필요) - - **Presence Intent** (선택 사항, 프레즌스 업데이트에만 필요) + - **Presence Intent** (선택 사항, 현재 상태 업데이트에만 필요) - **Bot** 페이지 위로 다시 스크롤하고 **Reset Token**을 클릭합니다. + **Bot** 페이지에서 다시 위로 스크롤한 뒤 **Reset Token**을 클릭합니다. - 이름과 달리, 이 작업은 첫 번째 토큰을 생성합니다. 실제로 "reset"되는 것은 없습니다. + 이름과 달리, 이 작업은 첫 토큰을 생성합니다. 아무것도 "재설정"되지 않습니다. - 토큰을 복사하여 어딘가에 저장합니다. 이것이 **Bot Token**이며 곧 필요합니다. + 토큰을 복사하여 어딘가에 저장합니다. 이것이 **봇 토큰**이며 곧 필요합니다. - - 사이드바에서 **OAuth2**를 클릭합니다. 봇을 서버에 추가할 수 있는 올바른 권한이 포함된 초대 URL을 생성합니다. + + 사이드바에서 **OAuth2**를 클릭합니다. 봇을 서버에 추가할 수 있는 적절한 권한이 포함된 초대 URL을 생성합니다. **OAuth2 URL Generator**까지 아래로 스크롤하고 다음을 활성화합니다. @@ -68,40 +68,40 @@ x-i18n: 아래에 **Bot Permissions** 섹션이 나타납니다. 최소한 다음을 활성화합니다. - **General Permissions** + **일반 권한** - 채널 보기 - **Text Permissions** + **텍스트 권한** - 메시지 보내기 - 메시지 기록 읽기 - 링크 임베드 - 파일 첨부 - 반응 추가 (선택 사항) - 이것은 일반 텍스트 채널을 위한 기본 권한 세트입니다. 포럼 또는 미디어 채널 워크플로를 포함하여 스레드를 만들거나 계속 이어가는 Discord 스레드에 게시할 계획이라면 **Send Messages in Threads**도 활성화합니다. - 하단에 생성된 URL을 복사해 브라우저에 붙여넣고, 서버를 선택한 다음 **Continue**를 클릭해 연결합니다. 이제 Discord 서버에서 봇을 볼 수 있어야 합니다. + 이는 일반 텍스트 채널을 위한 기본 권한 세트입니다. 스레드를 만들거나 이어가는 포럼 또는 미디어 채널 워크플로를 포함하여 Discord 스레드에 게시할 계획이라면 **스레드에서 메시지 보내기**도 활성화합니다. + 하단에 생성된 URL을 복사하여 브라우저에 붙여넣고, 서버를 선택한 다음 **Continue**를 클릭하여 연결합니다. 이제 Discord 서버에서 봇이 보일 것입니다. - Discord 앱으로 돌아가서 내부 ID를 복사할 수 있도록 개발자 모드를 활성화해야 합니다. + Discord 앱으로 돌아가 내부 ID를 복사할 수 있도록 개발자 모드를 활성화해야 합니다. - 1. **User Settings**(아바타 옆 톱니바퀴 아이콘) 클릭 → **Advanced** → **Developer Mode** 켜기 - 2. 사이드바에서 **server icon**을 오른쪽 클릭 → **Copy Server ID** - 3. **own avatar**를 오른쪽 클릭 → **Copy User ID** + 1. **User Settings**(아바타 옆 톱니바퀴 아이콘) → **Advanced**를 클릭하고 **Developer Mode**를 켭니다. + 2. 사이드바의 **서버 아이콘**을 오른쪽 클릭 → **Copy Server ID** + 3. **본인 아바타**를 오른쪽 클릭 → **Copy User ID** - **Server ID**와 **User ID**를 Bot Token과 함께 저장합니다. 다음 단계에서 이 세 가지를 모두 OpenClaw에 보냅니다. + **Server ID**와 **User ID**를 Bot Token과 함께 저장합니다. 다음 단계에서 세 가지를 모두 OpenClaw에 보냅니다. - 페어링이 작동하려면 Discord에서 봇이 사용자에게 DM을 보낼 수 있어야 합니다. **server icon**을 오른쪽 클릭 → **Privacy Settings** → **Direct Messages**를 켭니다. + 페어링이 작동하려면 Discord가 봇이 사용자에게 DM을 보낼 수 있도록 허용해야 합니다. **서버 아이콘**을 오른쪽 클릭 → **Privacy Settings** → **Direct Messages**를 켭니다. - 이렇게 하면 서버 멤버(봇 포함)가 사용자에게 DM을 보낼 수 있습니다. OpenClaw와 함께 Discord DM을 사용하려면 이 설정을 켜 둡니다. 길드 채널만 사용할 계획이라면 페어링 후 DM을 비활성화할 수 있습니다. + 이렇게 하면 봇을 포함한 서버 멤버가 사용자에게 DM을 보낼 수 있습니다. OpenClaw와 함께 Discord DM을 사용하려면 이 설정을 켜 둡니다. 길드 채널만 사용할 계획이라면 페어링 후 DM을 비활성화할 수 있습니다. - Discord 봇 토큰은 비밀 정보(비밀번호와 같음)입니다. 에이전트에게 메시지를 보내기 전에 OpenClaw가 실행되는 머신에서 설정합니다. + Discord 봇 토큰은 비밀번호와 같은 비밀 정보입니다. 에이전트에게 메시지를 보내기 전에 OpenClaw를 실행하는 머신에 설정합니다. ```bash export DISCORD_BOT_TOKEN="YOUR_BOT_TOKEN" @@ -121,8 +121,8 @@ openclaw gateway ``` OpenClaw가 이미 백그라운드 서비스로 실행 중이라면 OpenClaw Mac 앱을 통해 다시 시작하거나 `openclaw gateway run` 프로세스를 중지한 뒤 다시 시작합니다. - 관리형 서비스 설치의 경우 `DISCORD_BOT_TOKEN`이 있는 셸에서 `openclaw gateway install`을 실행하거나 변수를 `~/.openclaw/.env`에 저장하여, 재시작 후 서비스가 env SecretRef를 해석할 수 있게 합니다. - 호스트가 Discord의 시작 애플리케이션 조회에 의해 차단되거나 속도 제한을 받는 경우, 시작 시 해당 REST 호출을 건너뛸 수 있도록 Developer Portal의 Discord 애플리케이션/클라이언트 ID를 설정합니다. 기본 계정에는 `channels.discord.applicationId`를 사용하고, 여러 Discord 봇을 실행하는 경우 `channels.discord.accounts..applicationId`를 사용합니다. + 관리형 서비스 설치의 경우 `DISCORD_BOT_TOKEN`이 있는 셸에서 `openclaw gateway install`을 실행하거나 변수를 `~/.openclaw/.env`에 저장하여, 다시 시작한 뒤 서비스가 env SecretRef를 확인할 수 있게 합니다. + 호스트가 Discord의 시작 애플리케이션 조회에서 차단되었거나 속도 제한을 받는 경우, 시작 시 해당 REST 호출을 건너뛸 수 있도록 Developer Portal의 Discord 애플리케이션/클라이언트 ID를 설정합니다. 기본 계정에는 `channels.discord.applicationId`를 사용하고, 여러 Discord 봇을 실행할 때는 `channels.discord.accounts..applicationId`를 사용합니다. @@ -130,9 +130,9 @@ openclaw gateway - 기존 채널(예: Telegram)에서 OpenClaw 에이전트와 채팅하고 다음과 같이 말합니다. Discord가 첫 번째 채널이라면 대신 CLI / 구성 탭을 사용합니다. + 기존 채널(예: Telegram)에서 OpenClaw 에이전트와 채팅하고 알려줍니다. Discord가 첫 채널이라면 대신 CLI / 구성 탭을 사용합니다. - > "I already set my Discord bot token in config. Please finish Discord setup with User ID `` and Server ID ``." + > "이미 구성에 Discord 봇 토큰을 설정했습니다. User ID ``와 Server ID ``로 Discord 설정을 완료해 주세요." 파일 기반 구성을 선호한다면 다음을 설정합니다. @@ -152,15 +152,15 @@ openclaw gateway } ``` - 기본 계정을 위한 env 폴백: + 기본 계정의 env 대체값: ```bash DISCORD_BOT_TOKEN=... ``` - 스크립트 기반 또는 원격 설정의 경우 `openclaw config patch --file ./discord.patch.json5 --dry-run`으로 동일한 JSON5 블록을 작성한 다음 `--dry-run` 없이 다시 실행합니다. 일반 텍스트 `token` 값이 지원됩니다. env/file/exec provider 전반에서 `channels.discord.token`에 SecretRef 값도 지원됩니다. [비밀 정보 관리](/ko/gateway/secrets)를 참조하세요. + 스크립트 기반 또는 원격 설정의 경우 같은 JSON5 블록을 `openclaw config patch --file ./discord.patch.json5 --dry-run`으로 작성한 다음 `--dry-run` 없이 다시 실행합니다. 일반 텍스트 `token` 값이 지원됩니다. SecretRef 값도 env/file/exec 공급자 전반에서 `channels.discord.token`에 지원됩니다. [비밀 관리](/ko/gateway/secrets)를 참조하세요. - 여러 Discord 봇의 경우 각 봇 토큰과 애플리케이션 ID를 해당 계정 아래에 둡니다. 최상위 `channels.discord.applicationId`는 계정에 상속되므로, 모든 계정이 같은 애플리케이션 ID를 사용해야 할 때만 거기에 설정합니다. + 여러 Discord 봇의 경우 각 봇 토큰과 애플리케이션 ID를 해당 계정 아래에 유지합니다. 최상위 `channels.discord.applicationId`는 계정에 상속되므로 모든 계정이 같은 애플리케이션 ID를 사용해야 할 때만 그곳에 설정합니다. ```json5 { @@ -188,13 +188,13 @@ DISCORD_BOT_TOKEN=... - gateway가 실행될 때까지 기다린 다음 Discord에서 봇에게 DM을 보냅니다. 봇이 페어링 코드를 응답합니다. + Gateway가 실행될 때까지 기다린 다음 Discord에서 봇에게 DM을 보냅니다. 봇이 페어링 코드를 응답합니다. 기존 채널에서 에이전트에게 페어링 코드를 보냅니다. - > "Approve this Discord pairing code: ``" + > "이 Discord 페어링 코드를 승인해 주세요: ``" @@ -208,28 +208,28 @@ openclaw pairing approve discord 페어링 코드는 1시간 후 만료됩니다. - 이제 DM을 통해 Discord에서 에이전트와 채팅할 수 있어야 합니다. + 이제 Discord에서 DM을 통해 에이전트와 채팅할 수 있어야 합니다. -토큰 해석은 계정을 인식합니다. 구성 토큰 값은 env 폴백보다 우선합니다. `DISCORD_BOT_TOKEN`은 기본 계정에만 사용됩니다. -활성화된 두 Discord 계정이 같은 봇 토큰으로 해석되는 경우, OpenClaw는 해당 토큰에 대해 하나의 gateway 모니터만 시작합니다. 구성에서 제공된 토큰은 기본 env 폴백보다 우선하며, 그렇지 않으면 첫 번째 활성화된 계정이 우선하고 중복 계정은 비활성화된 것으로 보고됩니다. -고급 아웃바운드 호출(메시지 도구/채널 작업)의 경우, 호출별 명시적 `token`이 해당 호출에 사용됩니다. 이는 보내기 및 읽기/프로브 스타일 작업(예: 읽기/검색/가져오기/스레드/핀/권한)에 적용됩니다. 계정 정책/재시도 설정은 여전히 활성 런타임 스냅샷에서 선택된 계정에서 가져옵니다. +토큰 확인은 계정을 인식합니다. 구성 토큰 값이 env 대체값보다 우선합니다. `DISCORD_BOT_TOKEN`은 기본 계정에만 사용됩니다. +활성화된 두 Discord 계정이 같은 봇 토큰으로 확인되면 OpenClaw는 해당 토큰에 대해 Gateway 모니터를 하나만 시작합니다. 구성에서 제공된 토큰은 기본 env 대체값보다 우선합니다. 그렇지 않으면 첫 번째 활성화된 계정이 우선하며 중복 계정은 비활성화된 것으로 보고됩니다. +고급 아웃바운드 호출(메시지 도구/채널 작업)의 경우 명시적인 호출별 `token`이 해당 호출에 사용됩니다. 이는 전송 및 읽기/프로브 스타일 작업(예: 읽기/검색/가져오기/스레드/핀/권한)에 적용됩니다. 계정 정책/재시도 설정은 여전히 활성 런타임 스냅샷에서 선택된 계정에서 가져옵니다. -## 권장: 길드 워크스페이스 설정 +## 권장: 길드 작업 공간 설정 -DM이 작동하면 Discord 서버를 전체 워크스페이스로 설정할 수 있습니다. 각 채널은 자체 컨텍스트를 가진 자체 에이전트 세션을 갖습니다. 사용자와 봇만 있는 비공개 서버에 권장됩니다. +DM이 작동하면 Discord 서버를 전체 작업 공간으로 설정할 수 있으며, 각 채널은 고유한 컨텍스트를 가진 자체 에이전트 세션을 갖습니다. 사용자와 봇만 있는 비공개 서버에 권장됩니다. - - 이렇게 하면 에이전트가 DM뿐만 아니라 서버의 모든 채널에서 응답할 수 있습니다. + + 이렇게 하면 에이전트가 DM뿐 아니라 서버의 모든 채널에서 응답할 수 있습니다. - > "Add my Discord Server ID `` to the guild allowlist" + > "내 Discord Server ID ``를 길드 허용 목록에 추가해 주세요" @@ -254,16 +254,16 @@ DM이 작동하면 Discord 서버를 전체 워크스페이스로 설정할 수 - - 기본적으로 에이전트는 길드 채널에서 @mention된 경우에만 응답합니다. 비공개 서버라면 모든 메시지에 응답하게 하고 싶을 가능성이 큽니다. + + 기본적으로 에이전트는 길드 채널에서 @멘션되었을 때만 응답합니다. 비공개 서버라면 아마 모든 메시지에 응답하도록 하고 싶을 것입니다. - 길드 채널에서는 일반 어시스턴트 최종 답변이 기본적으로 비공개로 유지됩니다. 표시되는 Discord 출력은 `message` 도구로 명시적으로 보내야 하므로, 에이전트는 기본적으로 지켜보다가 채널 답장이 유용하다고 판단할 때만 게시할 수 있습니다. + 길드 채널에서 일반 어시스턴트 최종 응답은 기본적으로 비공개로 유지됩니다. 표시되는 Discord 출력은 `message` 도구로 명시적으로 보내야 하므로, 에이전트는 기본적으로 대기하다가 채널 응답이 유용하다고 판단할 때만 게시할 수 있습니다. - 즉, 선택한 모델이 도구를 안정적으로 호출해야 합니다. Discord에 입력 중 표시가 나타나고 로그에는 토큰 사용량이 보이지만 게시된 메시지가 없다면, 세션 로그에서 `didSendViaMessagingTool: false`가 있는 어시스턴트 텍스트를 확인하세요. 이는 모델이 `message(action=send)`를 호출하는 대신 비공개 최종 답변을 생성했다는 뜻입니다. 더 강력한 도구 호출 모델로 전환하거나, 아래 구성을 사용해 레거시 자동 최종 답변을 복원하세요. + 즉, 선택한 모델이 도구를 안정적으로 호출해야 합니다. Discord에 입력 중 표시가 나타나고 로그에 토큰 사용량이 보이지만 게시된 메시지가 없다면 세션 로그에서 `didSendViaMessagingTool: false`가 포함된 어시스턴트 텍스트를 확인하세요. 이는 모델이 `message(action=send)`를 호출하는 대신 비공개 최종 답변을 생성했다는 의미입니다. 더 강력한 도구 호출 모델로 전환하거나 아래 구성을 사용하여 레거시 자동 최종 응답을 복원하세요. - > "Allow my agent to respond on this server without having to be @mentioned" + > "내 에이전트가 이 서버에서 @멘션되지 않아도 응답할 수 있게 해 주세요" 길드 구성에서 `requireMention: false`를 설정합니다. @@ -282,7 +282,7 @@ DM이 작동하면 Discord 서버를 전체 워크스페이스로 설정할 수 } ``` - 그룹/채널 룸에 대한 레거시 자동 최종 답변을 복원하려면 `messages.groupChat.visibleReplies: "automatic"`을 설정합니다. + 그룹/채널 방에 대해 레거시 자동 최종 응답을 복원하려면 `messages.groupChat.visibleReplies: "automatic"`을 설정합니다. @@ -290,87 +290,87 @@ DM이 작동하면 Discord 서버를 전체 워크스페이스로 설정할 수 - 기본적으로 장기 메모리(MEMORY.md)는 DM 세션에서만 로드됩니다. 길드 채널은 MEMORY.md를 자동 로드하지 않습니다. + 기본적으로 장기 메모리(MEMORY.md)는 DM 세션에서만 로드됩니다. 길드 채널은 MEMORY.md를 자동으로 로드하지 않습니다. - > "When I ask questions in Discord channels, use memory_search or memory_get if you need long-term context from MEMORY.md." + > "Discord 채널에서 내가 질문할 때 MEMORY.md의 장기 컨텍스트가 필요하면 memory_search 또는 memory_get을 사용해 주세요." - 모든 채널에서 공유 컨텍스트가 필요하다면 안정적인 지침을 `AGENTS.md` 또는 `USER.md`에 넣습니다(모든 세션에 주입됨). 장기 노트는 `MEMORY.md`에 보관하고 필요할 때 메모리 도구로 접근합니다. + 모든 채널에 공유 컨텍스트가 필요하다면 안정적인 지침을 `AGENTS.md` 또는 `USER.md`에 넣으세요(모든 세션에 주입됩니다). 장기 메모는 `MEMORY.md`에 유지하고 필요할 때 메모리 도구로 접근합니다. -이제 Discord 서버에 몇 개의 채널을 만들고 채팅을 시작하세요. 에이전트는 채널 이름을 볼 수 있으며, 각 채널은 자체 격리 세션을 갖습니다. 따라서 `#coding`, `#home`, `#research` 또는 워크플로에 맞는 어떤 채널이든 설정할 수 있습니다. +이제 Discord 서버에 채널을 몇 개 만들고 채팅을 시작하세요. 에이전트는 채널 이름을 볼 수 있으며, 각 채널은 고유한 격리 세션을 갖습니다. 따라서 워크플로에 맞게 `#coding`, `#home`, `#research` 또는 원하는 채널을 설정할 수 있습니다. ## 런타임 모델 - Gateway가 Discord 연결을 소유합니다. - 답장 라우팅은 결정적입니다. Discord 인바운드 답장은 Discord로 돌아갑니다. -- Discord 길드/채널 메타데이터는 사용자가 볼 수 있는 답장 접두사가 아니라 신뢰할 수 없는 +- Discord 길드/채널 메타데이터는 사용자에게 표시되는 답장 접두사가 아니라 신뢰할 수 없는 컨텍스트로 모델 프롬프트에 추가됩니다. 모델이 해당 봉투를 다시 복사하면 - OpenClaw는 발신 답장과 향후 리플레이 컨텍스트에서 복사된 메타데이터를 제거합니다. -- 기본적으로(`session.dmScope=main`) 직접 채팅은 에이전트 메인 세션(`agent:main:main`)을 공유합니다. + OpenClaw는 아웃바운드 답장과 이후 리플레이 컨텍스트에서 복사된 메타데이터를 제거합니다. +- 기본적으로 (`session.dmScope=main`) 직접 채팅은 에이전트 메인 세션(`agent:main:main`)을 공유합니다. - 길드 채널은 격리된 세션 키입니다(`agent::discord:channel:`). - 그룹 DM은 기본적으로 무시됩니다(`channels.discord.dm.groupEnabled=false`). -- 네이티브 슬래시 명령은 격리된 명령 세션(`agent::discord:slash:`)에서 실행되지만, 라우팅된 대화 세션으로 `CommandTargetSessionKey`를 계속 전달합니다. -- Discord로 보내는 텍스트 전용 Cron/Heartbeat 알림 전달은 최종 - 어시스턴트 표시 답변을 한 번 사용합니다. 미디어와 구조화된 컴포넌트 페이로드는 - 에이전트가 전달 가능한 페이로드를 여러 개 내보내면 여러 메시지로 유지됩니다. +- 네이티브 슬래시 명령은 격리된 명령 세션(`agent::discord:slash:`)에서 실행되며, 라우팅된 대화 세션으로 `CommandTargetSessionKey`를 계속 전달합니다. +- Discord로 전달되는 텍스트 전용 Cron/Heartbeat 알림은 최종 + 어시스턴트 표시 답변을 한 번 사용합니다. 미디어 및 구조화된 컴포넌트 페이로드는 + 에이전트가 여러 전달 가능 페이로드를 내보낼 때 다중 메시지로 유지됩니다. ## 포럼 채널 -Discord 포럼 및 미디어 채널은 스레드 게시물만 허용합니다. OpenClaw는 이를 만드는 두 가지 방법을 지원합니다. +Discord 포럼 및 미디어 채널은 스레드 게시물만 허용합니다. OpenClaw는 이를 생성하는 두 가지 방법을 지원합니다. -- 포럼 부모(`channel:`)로 메시지를 보내 스레드를 자동 생성합니다. 스레드 제목은 메시지의 첫 번째 비어 있지 않은 줄을 사용합니다. -- `openclaw message thread create`를 사용해 스레드를 직접 만듭니다. 포럼 채널에는 `--message-id`를 전달하지 마세요. +- 포럼 상위 항목(`channel:`)으로 메시지를 보내 스레드를 자동 생성합니다. 스레드 제목은 메시지의 첫 번째 비어 있지 않은 줄을 사용합니다. +- `openclaw message thread create`를 사용해 스레드를 직접 생성합니다. 포럼 채널에는 `--message-id`를 전달하지 마세요. -예: 포럼 부모로 보내 스레드 만들기 +예: 포럼 상위 항목으로 보내 스레드 생성 ```bash openclaw message send --channel discord --target channel: \ --message "Topic title\nBody of the post" ``` -예: 포럼 스레드를 명시적으로 만들기 +예: 포럼 스레드를 명시적으로 생성 ```bash openclaw message thread create --channel discord --target channel: \ --thread-name "Topic title" --message "Body of the post" ``` -포럼 부모는 Discord 컴포넌트를 허용하지 않습니다. 컴포넌트가 필요하면 스레드 자체(`channel:`)로 보내세요. +포럼 상위 항목은 Discord 컴포넌트를 허용하지 않습니다. 컴포넌트가 필요하면 스레드 자체(`channel:`)로 보내세요. -## 인터랙티브 컴포넌트 +## 대화형 컴포넌트 OpenClaw는 에이전트 메시지용 Discord 컴포넌트 v2 컨테이너를 지원합니다. `components` 페이로드와 함께 메시지 도구를 사용하세요. 상호작용 결과는 일반 인바운드 메시지처럼 에이전트로 다시 라우팅되며 기존 Discord `replyToMode` 설정을 따릅니다. 지원되는 블록: - `text`, `section`, `separator`, `actions`, `media-gallery`, `file` -- 액션 행은 최대 5개의 버튼 또는 단일 선택 메뉴를 허용합니다. +- 작업 행은 최대 5개의 버튼 또는 단일 선택 메뉴를 허용합니다 - 선택 유형: `string`, `user`, `role`, `mentionable`, `channel` -기본적으로 컴포넌트는 한 번만 사용할 수 있습니다. 버튼, 선택 항목, 폼을 만료될 때까지 여러 번 사용할 수 있게 하려면 `components.reusable=true`를 설정하세요. +기본적으로 컴포넌트는 일회용입니다. 버튼, 선택 항목, 양식을 만료될 때까지 여러 번 사용할 수 있게 하려면 `components.reusable=true`를 설정하세요. 버튼을 클릭할 수 있는 사용자를 제한하려면 해당 버튼에 `allowedUsers`를 설정하세요(Discord 사용자 ID, 태그 또는 `*`). 구성된 경우 일치하지 않는 사용자는 임시 거부 메시지를 받습니다. -`/model` 및 `/models` 슬래시 명령은 제공자, 모델, 호환 런타임 드롭다운과 제출 단계가 있는 인터랙티브 모델 선택기를 엽니다. `/models add`는 더 이상 권장되지 않으며 이제 채팅에서 모델을 등록하는 대신 지원 중단 메시지를 반환합니다. 선택기 답장은 임시 메시지이며 호출한 사용자만 사용할 수 있습니다. +`/model` 및 `/models` 슬래시 명령은 제공자, 모델, 호환 런타임 드롭다운과 제출 단계가 있는 대화형 모델 선택기를 엽니다. `/models add`는 더 이상 사용되지 않으며 이제 채팅에서 모델을 등록하는 대신 지원 중단 메시지를 반환합니다. 선택기 답장은 임시 메시지이며 호출한 사용자만 사용할 수 있습니다. 파일 첨부: -- `file` 블록은 첨부 참조(`attachment://`)를 가리켜야 합니다. -- `media`/`path`/`filePath`로 첨부를 제공합니다(단일 파일). 여러 파일에는 `media-gallery`를 사용하세요. -- 업로드 이름이 첨부 참조와 일치해야 할 때 `filename`을 사용해 재정의하세요. +- `file` 블록은 첨부 참조(`attachment://`)를 가리켜야 합니다 +- `media`/`path`/`filePath`(단일 파일)를 통해 첨부를 제공하세요. 여러 파일에는 `media-gallery`를 사용하세요 +- 업로드 이름이 첨부 참조와 일치해야 하는 경우 `filename`을 사용해 재정의하세요 -모달 폼: +모달 양식: -- 최대 5개 필드와 함께 `components.modal`을 추가합니다. +- 최대 5개 필드와 함께 `components.modal`을 추가하세요 - 필드 유형: `text`, `checkbox`, `radio`, `select`, `role-select`, `user-select` -- OpenClaw가 트리거 버튼을 자동으로 추가합니다. +- OpenClaw는 트리거 버튼을 자동으로 추가합니다 예: @@ -426,41 +426,41 @@ OpenClaw는 에이전트 메시지용 Discord 컴포넌트 v2 컨테이너를 } ``` -## 접근 제어 및 라우팅 +## 액세스 제어 및 라우팅 - - `channels.discord.dmPolicy`는 DM 접근을 제어합니다. `channels.discord.allowFrom`은 표준 DM 허용 목록입니다. + + `channels.discord.dmPolicy`는 DM 액세스를 제어합니다. `channels.discord.allowFrom`은 표준 DM 허용 목록입니다. - `pairing`(기본값) - `allowlist` - `open`(`channels.discord.allowFrom`에 `"*"`가 포함되어야 함) - `disabled` - DM 정책이 열림이 아니면 알 수 없는 사용자는 차단됩니다(또는 `pairing` 모드에서는 페어링하라는 안내를 받습니다). + DM 정책이 열림이 아닌 경우, 알 수 없는 사용자는 차단됩니다(또는 `pairing` 모드에서는 페어링하라는 메시지가 표시됩니다). 다중 계정 우선순위: - `channels.discord.accounts.default.allowFrom`은 `default` 계정에만 적용됩니다. - - 계정이 하나인 경우 `allowFrom`이 레거시 `dm.allowFrom`보다 우선합니다. - - 명명된 계정은 자체 `allowFrom`과 레거시 `dm.allowFrom`이 설정되지 않은 경우 `channels.discord.allowFrom`을 상속합니다. - - 명명된 계정은 `channels.discord.accounts.default.allowFrom`을 상속하지 않습니다. + - 하나의 계정에서는 `allowFrom`이 레거시 `dm.allowFrom`보다 우선합니다. + - 이름이 지정된 계정은 자체 `allowFrom` 및 레거시 `dm.allowFrom`이 설정되지 않은 경우 `channels.discord.allowFrom`을 상속합니다. + - 이름이 지정된 계정은 `channels.discord.accounts.default.allowFrom`을 상속하지 않습니다. - 레거시 `channels.discord.dm.policy` 및 `channels.discord.dm.allowFrom`은 호환성을 위해 여전히 읽습니다. `openclaw doctor --fix`는 접근을 변경하지 않고 처리할 수 있을 때 이를 `dmPolicy`와 `allowFrom`으로 마이그레이션합니다. + 레거시 `channels.discord.dm.policy` 및 `channels.discord.dm.allowFrom`은 호환성을 위해 계속 읽습니다. `openclaw doctor --fix`는 액세스를 변경하지 않고 가능할 때 이를 `dmPolicy` 및 `allowFrom`으로 마이그레이션합니다. 전달용 DM 대상 형식: - `user:` - `<@id>` 멘션 - 기본 채널이 활성화된 경우 순수 숫자 ID는 일반적으로 채널 ID로 해석되지만, 계정의 유효 DM `allowFrom`에 나열된 ID는 호환성을 위해 사용자 DM 대상으로 처리됩니다. + 단순 숫자 ID는 일반적으로 채널 기본값이 활성 상태일 때 채널 ID로 해석되지만, 계정의 유효 DM `allowFrom`에 나열된 ID는 호환성을 위해 사용자 DM 대상으로 취급됩니다. - + Discord DM은 `channels.discord.allowFrom`에서 동적 `accessGroup:` 항목을 사용할 수 있습니다. - 접근 그룹 이름은 메시지 채널 간에 공유됩니다. 멤버가 각 채널의 일반 `allowFrom` 구문으로 표현되는 정적 그룹에는 `type: "message.senders"`를 사용하고, Discord 채널의 현재 `ViewChannel` 대상자가 멤버십을 동적으로 정의해야 할 때는 `type: "discord.channelAudience"`를 사용하세요. 공유 접근 그룹 동작은 여기에 문서화되어 있습니다: [접근 그룹](/ko/channels/access-groups) + 액세스 그룹 이름은 메시지 채널 간에 공유됩니다. 멤버가 각 채널의 일반 `allowFrom` 구문으로 표현되는 정적 그룹에는 `type: "message.senders"`를 사용하고, Discord 채널의 현재 `ViewChannel` 대상자가 멤버십을 동적으로 정의해야 할 때는 `type: "discord.channelAudience"`를 사용하세요. 공유 액세스 그룹 동작은 여기 문서에 설명되어 있습니다. [액세스 그룹](/ko/channels/access-groups) ```json5 { @@ -483,9 +483,9 @@ OpenClaw는 에이전트 메시지용 Discord 컴포넌트 v2 컨테이너를 } ``` - Discord 텍스트 채널에는 별도의 멤버 목록이 없습니다. `type: "discord.channelAudience"`는 멤버십을 다음과 같이 모델링합니다. DM 발신자는 구성된 길드의 멤버이며, 역할 및 채널 덮어쓰기가 적용된 후 구성된 채널에 대해 현재 유효한 `ViewChannel` 권한을 가지고 있습니다. + Discord 텍스트 채널에는 별도의 멤버 목록이 없습니다. `type: "discord.channelAudience"`는 멤버십을 다음과 같이 모델링합니다. DM 발신자는 구성된 길드의 멤버이고, 역할 및 채널 덮어쓰기가 적용된 후 구성된 채널에 대해 현재 유효한 `ViewChannel` 권한을 가지고 있습니다. - 예: 다른 모든 사람에게는 DM을 닫아 둔 상태에서 `#maintainers`를 볼 수 있는 누구나 봇에게 DM을 보낼 수 있게 허용합니다. + 예: 다른 모든 사용자에게는 DM을 닫아 두면서, `#maintainers`를 볼 수 있는 모든 사람이 봇에 DM을 보낼 수 있도록 허용합니다. ```json5 { @@ -506,7 +506,7 @@ OpenClaw는 에이전트 메시지용 Discord 컴포넌트 v2 컨테이너를 } ``` - 동적 항목과 정적 항목을 섞을 수 있습니다. + 동적 항목과 정적 항목을 함께 사용할 수 있습니다. ```json5 { @@ -526,29 +526,29 @@ OpenClaw는 에이전트 메시지용 Discord 컴포넌트 v2 컨테이너를 } ``` - 조회는 실패 시 닫힘으로 처리됩니다. Discord가 `Missing Access`를 반환하거나, 멤버 조회가 실패하거나, 채널이 다른 길드에 속하면 DM 발신자는 권한 없음으로 처리됩니다. + 조회는 실패 시 닫힘으로 처리됩니다. Discord가 `Missing Access`를 반환하거나, 멤버 조회가 실패하거나, 채널이 다른 길드에 속한 경우 DM 발신자는 권한이 없는 것으로 처리됩니다. - 채널 대상자 접근 그룹을 사용할 때 봇에 대해 Discord Developer Portal **서버 멤버 인텐트**를 활성화하세요. DM에는 길드 멤버 상태가 포함되지 않으므로 OpenClaw는 승인 시점에 Discord REST를 통해 멤버를 확인합니다. + 채널 대상자 액세스 그룹을 사용할 때 봇에 대해 Discord Developer Portal **Server Members Intent**를 활성화하세요. DM에는 길드 멤버 상태가 포함되지 않으므로, OpenClaw는 권한 부여 시점에 Discord REST를 통해 멤버를 확인합니다. - + 길드 처리는 `channels.discord.groupPolicy`로 제어됩니다. - `open` - `allowlist` - `disabled` - `channels.discord`가 존재할 때 보안 기준선은 `allowlist`입니다. + `channels.discord`가 있을 때 보안 기준선은 `allowlist`입니다. `allowlist` 동작: - - 길드는 `channels.discord.guilds`와 일치해야 합니다(`id` 권장, 슬러그 허용). - - 선택적 발신자 허용 목록: `users`(안정적인 ID 권장) 및 `roles`(역할 ID만). 둘 중 하나가 구성된 경우 발신자가 `users` 또는 `roles`와 일치하면 허용됩니다. - - 직접 이름/태그 일치는 기본적으로 비활성화됩니다. 비상 호환 모드로만 `channels.discord.dangerouslyAllowNameMatching: true`를 활성화하세요. - - `users`에는 이름/태그가 지원되지만 ID가 더 안전합니다. 이름/태그 항목이 사용되면 `openclaw security audit`가 경고합니다. - - 길드에 `channels`가 구성되어 있으면 목록에 없는 채널은 거부됩니다. - - 길드에 `channels` 블록이 없으면 해당 허용 목록 길드의 모든 채널이 허용됩니다. + - 길드는 `channels.discord.guilds`와 일치해야 합니다(`id` 권장, 슬러그 허용) + - 선택적 발신자 허용 목록: `users`(안정적인 ID 권장) 및 `roles`(역할 ID만). 둘 중 하나가 구성된 경우 발신자는 `users` 또는 `roles`와 일치할 때 허용됩니다 + - 직접 이름/태그 매칭은 기본적으로 비활성화되어 있습니다. 비상 호환 모드로만 `channels.discord.dangerouslyAllowNameMatching: true`를 활성화하세요 + - `users`에는 이름/태그가 지원되지만 ID가 더 안전합니다. 이름/태그 항목이 사용되면 `openclaw security audit`가 경고합니다 + - 길드에 `channels`가 구성된 경우 목록에 없는 채널은 거부됩니다 + - 길드에 `channels` 블록이 없으면 해당 허용 목록 길드의 모든 채널이 허용됩니다 예: @@ -574,23 +574,23 @@ OpenClaw는 에이전트 메시지용 Discord 컴포넌트 v2 컨테이너를 } ``` - `DISCORD_BOT_TOKEN`만 설정하고 `channels.discord` 블록을 만들지 않으면 런타임 폴백은 `channels.defaults.groupPolicy`가 `open`이더라도 `groupPolicy="allowlist"`입니다(로그에 경고 표시). + `DISCORD_BOT_TOKEN`만 설정하고 `channels.discord` 블록을 생성하지 않으면, 런타임 대체값은 `channels.defaults.groupPolicy`가 `open`이더라도 `groupPolicy="allowlist"`입니다(로그에 경고가 표시됨). - - 길드 메시지는 기본적으로 멘션 게이트가 적용됩니다. + + 길드 메시지는 기본적으로 멘션으로 제한됩니다. 멘션 감지에는 다음이 포함됩니다. - 명시적 봇 멘션 - - 구성된 멘션 패턴(`agents.list[].groupChat.mentionPatterns`, 폴백 `messages.groupChat.mentionPatterns`) + - 구성된 멘션 패턴(`agents.list[].groupChat.mentionPatterns`, 대체값 `messages.groupChat.mentionPatterns`) - 지원되는 경우의 암시적 봇 답장 동작 - 발신 Discord 메시지를 작성할 때 표준 멘션 구문을 사용하세요. 사용자는 `<@USER_ID>`, 채널은 `<#CHANNEL_ID>`, 역할은 `<@&ROLE_ID>`입니다. 레거시 `<@!USER_ID>` 닉네임 멘션 형식을 사용하지 마세요. + 아웃바운드 Discord 메시지를 작성할 때 표준 멘션 구문을 사용하세요. 사용자는 `<@USER_ID>`, 채널은 `<#CHANNEL_ID>`, 역할은 `<@&ROLE_ID>`입니다. 레거시 `<@!USER_ID>` 닉네임 멘션 형식을 사용하지 마세요. `requireMention`은 길드/채널별로 구성됩니다(`channels.discord.guilds...`). - `ignoreOtherMentions`는 선택적으로 봇이 아닌 다른 사용자/역할을 멘션하는 메시지를 드롭합니다(@everyone/@here 제외). + `ignoreOtherMentions`는 봇이 아닌 다른 사용자/역할을 멘션하는 메시지를 선택적으로 삭제합니다(@everyone/@here 제외). 그룹 DM: @@ -602,7 +602,7 @@ OpenClaw는 에이전트 메시지용 Discord 컴포넌트 v2 컨테이너를 ### 역할 기반 에이전트 라우팅 -`bindings[].match.roles`를 사용해 Discord 길드 멤버를 역할 ID별로 다른 에이전트에 라우팅합니다. 역할 기반 바인딩은 역할 ID만 허용하며 피어 또는 부모 피어 바인딩 이후, 길드 전용 바인딩 이전에 평가됩니다. 바인딩이 다른 일치 필드도 설정하는 경우(예: `peer` + `guildId` + `roles`) 구성된 모든 필드가 일치해야 합니다. +`bindings[].match.roles`를 사용해 Discord 길드 멤버를 역할 ID별로 다른 에이전트에 라우팅하세요. 역할 기반 바인딩은 역할 ID만 허용하며 피어 또는 부모 피어 바인딩 이후, 길드 전용 바인딩 이전에 평가됩니다. 바인딩이 다른 매치 필드도 설정한 경우(예: `peer` + `guildId` + `roles`), 구성된 모든 필드가 일치해야 합니다. ```json5 { @@ -628,13 +628,13 @@ OpenClaw는 에이전트 메시지용 Discord 컴포넌트 v2 컨테이너를 ## 네이티브 명령 및 명령 인증 -- `commands.native`의 기본값은 `"auto"`이며 Discord에서 활성화됩니다. +- `commands.native`는 기본값이 `"auto"`이며 Discord에서 활성화됩니다. - 채널별 재정의: `channels.discord.commands.native`. - `commands.native=false`는 시작 중 Discord 슬래시 명령 등록 및 정리를 건너뜁니다. 이전에 등록된 명령은 Discord 앱에서 제거할 때까지 Discord에 계속 표시될 수 있습니다. - 네이티브 명령 인증은 일반 메시지 처리와 동일한 Discord 허용 목록/정책을 사용합니다. -- 권한이 없는 사용자에게도 명령이 Discord UI에 계속 표시될 수 있습니다. 실행 시에는 여전히 OpenClaw 인증을 적용하며 "권한 없음"을 반환합니다. +- 권한이 없는 사용자에게도 Discord UI에서 명령이 계속 표시될 수 있습니다. 실행 시에는 여전히 OpenClaw 인증을 적용하며 "not authorized"를 반환합니다. -명령 카탈로그와 동작은 [슬래시 명령](/ko/tools/slash-commands)을 참고하세요. +명령 카탈로그와 동작은 [슬래시 명령](/ko/tools/slash-commands)을 참조하세요. 기본 슬래시 명령 설정: @@ -643,7 +643,7 @@ OpenClaw는 에이전트 메시지용 Discord 컴포넌트 v2 컨테이너를 ## 기능 세부 정보 - + Discord는 에이전트 출력에서 답장 태그를 지원합니다. - `[[reply_to_current]]` @@ -657,17 +657,17 @@ OpenClaw는 에이전트 메시지용 Discord 컴포넌트 v2 컨테이너를 - `batched` 참고: `off`는 암시적 답장 스레딩을 비활성화합니다. 명시적 `[[reply_to_*]]` 태그는 계속 적용됩니다. - `first`는 항상 해당 턴의 첫 번째 발신 Discord 메시지에 암시적 네이티브 답장 참조를 연결합니다. - `batched`는 수신 턴이 여러 메시지의 디바운스된 배치였을 때만 Discord의 암시적 네이티브 답장 참조를 연결합니다. 이는 모든 단일 메시지 턴이 아니라, 주로 모호한 폭주 채팅에 네이티브 답장을 사용하려는 경우 유용합니다. + `first`는 항상 해당 턴의 첫 번째 발신 Discord 메시지에 암시적 네이티브 답장 참조를 첨부합니다. + `batched`는 수신 턴이 여러 메시지의 디바운스된 배치였을 때만 Discord의 암시적 네이티브 답장 참조를 첨부합니다. 이는 모든 단일 메시지 턴이 아니라, 주로 모호하고 짧은 시간에 몰리는 채팅에 네이티브 답장을 사용하려는 경우 유용합니다. - 메시지 ID는 컨텍스트/기록에 노출되므로 에이전트가 특정 메시지를 대상으로 지정할 수 있습니다. + 메시지 ID는 컨텍스트/기록에 노출되어 에이전트가 특정 메시지를 대상으로 지정할 수 있습니다. - - OpenClaw는 임시 메시지를 보내고 텍스트가 도착하는 동안 이를 편집하여 초안 답장을 스트리밍할 수 있습니다. `channels.discord.streaming`은 `off` (기본값) | `partial` | `block` | `progress`를 받습니다. `progress`는 편집 가능한 상태 초안 하나를 유지하고 최종 전달 전까지 도구 진행 상황으로 업데이트합니다. `streamMode`는 레거시 별칭이며 자동 마이그레이션됩니다. + + OpenClaw는 임시 메시지를 보내고 텍스트가 도착하는 동안 이를 편집하여 초안 답장을 스트리밍할 수 있습니다. `channels.discord.streaming`은 `off` (기본값) | `partial` | `block` | `progress`를 받습니다. `progress`는 편집 가능한 상태 초안 하나를 유지하고 최종 전달 전까지 도구 진행 상황으로 업데이트합니다. `streamMode`는 기존 별칭이며 자동 마이그레이션됩니다. - 여러 봇 또는 Gateway가 계정을 공유할 때 Discord 미리보기 편집은 속도 제한에 빠르게 도달하므로 기본값은 `off`로 유지됩니다. + 여러 봇이나 gateways가 계정을 공유할 때 Discord 미리보기 편집이 빠르게 속도 제한에 걸리므로 기본값은 `off`로 유지됩니다. ```json5 { @@ -685,15 +685,34 @@ OpenClaw는 에이전트 메시지용 Discord 컴포넌트 v2 컨테이너를 ``` - `partial`은 토큰이 도착하는 동안 단일 미리보기 메시지를 편집합니다. - - `block`은 초안 크기의 청크를 내보냅니다. 크기와 중단 지점을 조정하려면 `draftChunk`를 사용하며, `textChunkLimit`로 제한됩니다. + - `block`은 초안 크기의 청크를 내보냅니다(`draftChunk`를 사용해 크기와 중단 지점을 조정하며, `textChunkLimit`로 제한됨). - 미디어, 오류, 명시적 답장 최종 메시지는 대기 중인 미리보기 편집을 취소합니다. - - `streaming.preview.toolProgress` (기본값 `true`)는 도구/진행 상황 업데이트가 미리보기 메시지를 재사용할지 제어합니다. + - `streaming.preview.toolProgress`(기본값 `true`)는 도구/진행 상황 업데이트가 미리보기 메시지를 재사용할지 제어합니다. + - `streaming.preview.commandText` / `streaming.progress.commandText`는 압축된 진행 상황 줄의 명령/실행 세부 정보를 제어합니다: `raw`(기본값) 또는 `status`(도구 레이블만). - 미리보기 스트리밍은 텍스트 전용입니다. 미디어 답장은 일반 전달로 폴백됩니다. `block` 스트리밍이 명시적으로 활성화된 경우, OpenClaw는 이중 스트리밍을 피하기 위해 미리보기 스트림을 건너뜁니다. + 압축된 진행 상황 줄은 유지하면서 원시 명령/실행 텍스트를 숨깁니다. + + ```json + { + "channels": { + "discord": { + "streaming": { + "mode": "progress", + "progress": { + "toolProgress": true, + "commandText": "status" + } + } + } + } + } + ``` + + 미리보기 스트리밍은 텍스트 전용입니다. 미디어 답장은 일반 전달로 폴백됩니다. `block` 스트리밍이 명시적으로 활성화된 경우 OpenClaw는 이중 스트리밍을 피하기 위해 미리보기 스트림을 건너뜁니다. - + 길드 기록 컨텍스트: - `channels.discord.historyLimit` 기본값 `20` @@ -707,26 +726,26 @@ OpenClaw는 에이전트 메시지용 Discord 컴포넌트 v2 컨테이너를 스레드 동작: - - Discord 스레드는 채널 세션으로 라우팅되며, 재정의하지 않는 한 상위 채널 구성을 상속합니다. - - 스레드 세션은 상위 채널의 세션 수준 `/model` 선택을 모델 전용 폴백으로 상속합니다. 스레드 로컬 `/model` 선택이 여전히 우선하며, transcript 상속이 활성화되지 않는 한 상위 transcript 기록은 복사되지 않습니다. - - `channels.discord.thread.inheritParent` (기본값 `false`)는 새 자동 스레드가 상위 transcript에서 시드되도록 선택합니다. 계정별 재정의는 `channels.discord.accounts..thread.inheritParent` 아래에 있습니다. + - Discord 스레드는 채널 세션으로 라우팅되며, 재정의하지 않는 한 부모 채널 구성을 상속합니다. + - 스레드 세션은 모델 전용 폴백으로 부모 채널의 세션 수준 `/model` 선택을 상속합니다. 스레드 로컬 `/model` 선택이 여전히 우선하며, 대화 기록 상속이 활성화되지 않는 한 부모 transcript 기록은 복사되지 않습니다. + - `channels.discord.thread.inheritParent`(기본값 `false`)는 새 자동 스레드가 부모 transcript에서 시드를 받도록 선택합니다. 계정별 재정의는 `channels.discord.accounts..thread.inheritParent` 아래에 있습니다. - 메시지 도구 반응은 `user:` DM 대상을 확인할 수 있습니다. - `guilds..channels..requireMention: false`는 답장 단계 활성화 폴백 중에도 유지됩니다. - 채널 주제는 **신뢰할 수 없는** 컨텍스트로 주입됩니다. 허용 목록은 에이전트를 트리거할 수 있는 사람을 제한하지만, 전체 보조 컨텍스트 삭제 경계는 아닙니다. + 채널 주제는 **신뢰할 수 없는** 컨텍스트로 주입됩니다. 허용 목록은 누가 에이전트를 트리거할 수 있는지를 제한하며, 완전한 보조 컨텍스트 비식별화 경계가 아닙니다. - - Discord는 스레드를 세션 대상에 바인딩하여 해당 스레드의 후속 메시지가 동일한 세션(서브에이전트 세션 포함)으로 계속 라우팅되도록 할 수 있습니다. + + Discord는 스레드를 세션 대상에 바인딩하여 해당 스레드의 후속 메시지가 동일한 세션(하위 에이전트 세션 포함)으로 계속 라우팅되도록 할 수 있습니다. 명령: - - `/focus ` 현재/새 스레드를 서브에이전트/세션 대상에 바인딩 + - `/focus ` 현재/새 스레드를 하위 에이전트/세션 대상에 바인딩 - `/unfocus` 현재 스레드 바인딩 제거 - `/agents` 활성 실행 및 바인딩 상태 표시 - - `/session idle ` 포커스된 바인딩의 비활동 자동 언포커스 조회/업데이트 - - `/session max-age ` 포커스된 바인딩의 하드 최대 수명 조회/업데이트 + - `/session idle ` 포커스된 바인딩의 비활성 자동 포커스 해제를 조회/업데이트 + - `/session max-age ` 포커스된 바인딩의 강제 최대 수명을 조회/업데이트 구성: @@ -757,17 +776,17 @@ OpenClaw는 에이전트 메시지용 Discord 컴포넌트 v2 컨테이너를 - `session.threadBindings.*`는 전역 기본값을 설정합니다. - `channels.discord.threadBindings.*`는 Discord 동작을 재정의합니다. - - `spawnSessions`는 `sessions_spawn({ thread: true })` 및 ACP 스레드 생성에 대해 스레드 자동 생성/바인딩을 제어합니다. 기본값: `true`. - - `defaultSpawnContext`는 스레드 바인딩 생성의 네이티브 서브에이전트 컨텍스트를 제어합니다. 기본값: `"fork"`. + - `spawnSessions`는 `sessions_spawn({ thread: true })` 및 ACP 스레드 생성에 대한 자동 생성/바인딩 스레드를 제어합니다. 기본값: `true`. + - `defaultSpawnContext`는 스레드 바인딩 생성의 네이티브 하위 에이전트 컨텍스트를 제어합니다. 기본값: `"fork"`. - 더 이상 사용되지 않는 `spawnSubagentSessions`/`spawnAcpSessions` 키는 `openclaw doctor --fix`로 마이그레이션됩니다. - 계정에 대해 스레드 바인딩이 비활성화된 경우 `/focus` 및 관련 스레드 바인딩 작업을 사용할 수 없습니다. - [서브에이전트](/ko/tools/subagents), [ACP 에이전트](/ko/tools/acp-agents), [구성 참조](/ko/gateway/configuration-reference)를 참고하세요. + [하위 에이전트](/ko/tools/subagents), [ACP 에이전트](/ko/tools/acp-agents), [구성 참조](/ko/gateway/configuration-reference)를 참조하세요. - - 안정적인 "항상 켜짐" ACP 작업 공간의 경우 Discord 대화를 대상으로 하는 최상위 typed ACP 바인딩을 구성하세요. + + 안정적인 "상시 실행" ACP 작업 공간의 경우 Discord 대화를 대상으로 하는 최상위 typed ACP 바인딩을 구성하세요. 구성 경로: @@ -823,27 +842,27 @@ OpenClaw는 에이전트 메시지용 Discord 컴포넌트 v2 컨테이너를 참고: - - `/acp spawn codex --bind here`는 현재 채널 또는 스레드를 제자리에서 바인딩하고 향후 메시지를 동일한 ACP 세션에 유지합니다. 스레드 메시지는 상위 채널 바인딩을 상속합니다. - - 바인딩된 채널 또는 스레드에서 `/new` 및 `/reset`은 동일한 ACP 세션을 제자리에서 재설정합니다. 임시 스레드 바인딩은 활성 상태일 때 대상 확인을 재정의할 수 있습니다. - - `spawnSessions`는 `--thread auto|here`를 통한 하위 스레드 생성/바인딩을 제한합니다. + - `/acp spawn codex --bind here`는 현재 채널 또는 스레드를 제자리에서 바인딩하고 이후 메시지를 동일한 ACP 세션에 유지합니다. 스레드 메시지는 부모 채널 바인딩을 상속합니다. + - 바인딩된 채널 또는 스레드에서 `/new` 및 `/reset`은 동일한 ACP 세션을 제자리에서 재설정합니다. 임시 스레드 바인딩은 활성 상태인 동안 대상 확인을 재정의할 수 있습니다. + - `spawnSessions`는 `--thread auto|here`를 통한 자식 스레드 생성/바인딩을 제한합니다. - 바인딩 동작 세부 정보는 [ACP 에이전트](/ko/tools/acp-agents)를 참고하세요. + 바인딩 동작 세부 정보는 [ACP 에이전트](/ko/tools/acp-agents)를 참조하세요. - + 길드별 반응 알림 모드: - `off` - `own` (기본값) - `all` - - `allowlist` (`guilds..users` 사용) + - `allowlist`(`guilds..users` 사용) 반응 이벤트는 시스템 이벤트로 변환되어 라우팅된 Discord 세션에 첨부됩니다. - + `ackReaction`은 OpenClaw가 수신 메시지를 처리하는 동안 확인 이모지를 보냅니다. 확인 순서: @@ -851,7 +870,7 @@ OpenClaw는 에이전트 메시지용 Discord 컴포넌트 v2 컨테이너를 - `channels.discord.accounts..ackReaction` - `channels.discord.ackReaction` - `messages.ackReaction` - - 에이전트 ID 이모지 폴백 (`agents.list[].identity.emoji`, 없으면 "👀") + - 에이전트 ID 이모지 폴백(`agents.list[].identity.emoji`, 없으면 "👀") 참고: @@ -860,10 +879,10 @@ OpenClaw는 에이전트 메시지용 Discord 컴포넌트 v2 컨테이너를 - - 채널에서 시작한 구성 쓰기는 기본적으로 활성화되어 있습니다. + + 채널에서 시작되는 구성 쓰기는 기본적으로 활성화됩니다. - 이는 `/config set|unset` 흐름에 영향을 줍니다(명령 기능이 활성화된 경우). + 이는 `/config set|unset` 흐름(명령 기능이 활성화된 경우)에 영향을 줍니다. 비활성화: @@ -879,8 +898,8 @@ OpenClaw는 에이전트 메시지용 Discord 컴포넌트 v2 컨테이너를 - - `channels.discord.proxy`를 사용해 Discord Gateway WebSocket 트래픽과 시작 REST 조회(애플리케이션 ID + 허용 목록 확인)를 HTTP(S) 프록시를 통해 라우팅합니다. + + `channels.discord.proxy`를 사용해 Discord gateway WebSocket 트래픽과 시작 시 REST 조회(애플리케이션 ID + 허용 목록 확인)를 HTTP(S) 프록시를 통해 라우팅합니다. ```json5 { @@ -910,7 +929,7 @@ OpenClaw는 에이전트 메시지용 Discord 컴포넌트 v2 컨테이너를 - + 프록시된 메시지를 시스템 멤버 ID에 매핑하려면 PluralKit 확인을 활성화하세요. ```json5 @@ -929,14 +948,14 @@ OpenClaw는 에이전트 메시지용 Discord 컴포넌트 v2 컨테이너를 참고: - 허용 목록은 `pk:`를 사용할 수 있습니다. - - 멤버 표시 이름은 `channels.discord.dangerouslyAllowNameMatching: true`일 때만 이름/슬러그로 매칭됩니다. + - 멤버 표시 이름은 `channels.discord.dangerouslyAllowNameMatching: true`일 때만 이름/슬러그로 일치됩니다. - 조회는 원본 메시지 ID를 사용하며 시간 창으로 제한됩니다. - - 조회에 실패하면, `allowBots=true`가 아닌 한 프록시된 메시지는 봇 메시지로 처리되어 삭제됩니다. + - 조회가 실패하면 프록시된 메시지는 봇 메시지로 처리되어 `allowBots=true`가 아닌 한 삭제됩니다. - - 에이전트가 알려진 Discord 사용자에 대해 결정론적인 발신 멘션이 필요할 때 `mentionAliases`를 사용하세요. 키는 앞의 `@`가 없는 핸들이며, 값은 Discord 사용자 ID입니다. 알 수 없는 핸들, `@everyone`, `@here`, Markdown 코드 스팬 안의 멘션은 변경되지 않습니다. + + 에이전트가 알려진 Discord 사용자에 대해 결정적인 발신 멘션이 필요할 때 `mentionAliases`를 사용하세요. 키는 앞의 `@`가 없는 핸들이며, 값은 Discord 사용자 ID입니다. 알 수 없는 핸들, `@everyone`, `@here`, Markdown 코드 스팬 안의 멘션은 변경되지 않습니다. ```json5 { @@ -959,8 +978,8 @@ OpenClaw는 에이전트 메시지용 Discord 컴포넌트 v2 컨테이너를 - - 상태 또는 활동 필드를 설정하거나 자동 프레즌스를 활성화하면 프레즌스 업데이트가 적용됩니다. + + 상태 또는 활동 필드를 설정하거나 자동 presence를 활성화하면 presence 업데이트가 적용됩니다. 상태만 사용하는 예시: @@ -1006,11 +1025,11 @@ OpenClaw는 에이전트 메시지용 Discord 컴포넌트 v2 컨테이너를 - 0: 플레이 중 - 1: 스트리밍 중(`activityUrl` 필요) - 2: 듣는 중 - - 3: 보는 중 - - 4: 사용자 지정(활동 텍스트를 상태 state로 사용, 이모지는 선택 사항) + - 3: 시청 중 + - 4: 사용자 지정(활동 텍스트를 상태 값으로 사용, 이모지는 선택 사항) - 5: 경쟁 중 - 자동 프레즌스 예시(런타임 상태 신호): + 자동 presence 예시(런타임 상태 신호): ```json5 { @@ -1027,58 +1046,59 @@ OpenClaw는 에이전트 메시지용 Discord 컴포넌트 v2 컨테이너를 } ``` - 자동 프레즌스는 런타임 가용성을 Discord 상태에 매핑합니다. 정상 => 온라인, 성능 저하 또는 알 수 없음 => 자리 비움, 소진 또는 사용 불가 => 방해 금지. 선택적 텍스트 재정의: + 자동 presence는 런타임 가용성을 Discord 상태에 매핑합니다. healthy => online, degraded 또는 unknown => idle, exhausted 또는 unavailable => dnd. 선택적 텍스트 재정의: - `autoPresence.healthyText` - `autoPresence.degradedText` - - `autoPresence.exhaustedText` (`{reason}` 플레이스홀더 지원) + - `autoPresence.exhaustedText` (`{reason}` placeholder 지원) - - Discord는 DM에서 버튼 기반 승인 처리를 지원하며, 선택적으로 원본 채널에 승인 프롬프트를 게시할 수 있습니다. + + Discord는 DM에서 버튼 기반 승인 처리를 지원하며, 선택적으로 원래 채널에 승인 프롬프트를 게시할 수 있습니다. - 구성 경로: + Config 경로: - `channels.discord.execApprovals.enabled` - - `channels.discord.execApprovals.approvers` (선택 사항; 가능한 경우 `commands.ownerAllowFrom`으로 대체) + - `channels.discord.execApprovals.approvers` (선택 사항; 가능한 경우 `commands.ownerAllowFrom`으로 fallback) - `channels.discord.execApprovals.target` (`dm` | `channel` | `both`, 기본값: `dm`) - `agentFilter`, `sessionFilter`, `cleanupAfterResolve` - Discord는 `enabled`가 설정되지 않았거나 `"auto"`이고 `execApprovals.approvers` 또는 `commands.ownerAllowFrom`에서 승인자를 하나 이상 확인할 수 있으면 네이티브 exec 승인을 자동으로 활성화합니다. Discord는 채널 `allowFrom`, 기존 `dm.allowFrom`, 또는 다이렉트 메시지 `defaultTo`에서 exec 승인자를 추론하지 않습니다. Discord를 네이티브 승인 클라이언트로 명시적으로 비활성화하려면 `enabled: false`를 설정하세요. + Discord는 `enabled`가 설정되지 않았거나 `"auto"`이고 `execApprovals.approvers` 또는 `commands.ownerAllowFrom`에서 하나 이상의 승인자를 확인할 수 있으면 native exec approvals를 자동으로 활성화합니다. Discord는 channel `allowFrom`, legacy `dm.allowFrom` 또는 direct-message `defaultTo`에서 exec 승인자를 추론하지 않습니다. Discord를 native approval 클라이언트로 명시적으로 비활성화하려면 `enabled: false`를 설정하세요. - `/diagnostics` 및 `/export-trajectory` 같은 민감한 소유자 전용 그룹 명령의 경우 OpenClaw는 승인 프롬프트와 최종 결과를 비공개로 보냅니다. 호출한 소유자에게 Discord 소유자 경로가 있으면 Discord DM을 먼저 시도하고, 사용할 수 없으면 Telegram 같은 `commands.ownerAllowFrom`의 첫 번째 사용 가능한 소유자 경로로 대체합니다. + `/diagnostics` 및 `/export-trajectory` 같은 민감한 owner-only group 명령의 경우 OpenClaw는 승인 프롬프트와 최종 결과를 비공개로 보냅니다. 호출한 owner에게 Discord owner route가 있으면 먼저 Discord DM을 시도합니다. 사용할 수 없으면 Telegram 같은 `commands.ownerAllowFrom`의 첫 번째 사용 가능한 owner route로 fallback합니다. - `target`이 `channel` 또는 `both`이면 승인 프롬프트가 채널에 표시됩니다. 확인된 승인자만 버튼을 사용할 수 있으며, 다른 사용자는 일시적인 거부 메시지를 받습니다. 승인 프롬프트에는 명령 텍스트가 포함되므로 신뢰할 수 있는 채널에서만 채널 전달을 활성화하세요. 세션 키에서 채널 ID를 파생할 수 없으면 OpenClaw는 DM 전달로 대체합니다. + `target`이 `channel` 또는 `both`이면 승인 프롬프트가 채널에 표시됩니다. 확인된 승인자만 버튼을 사용할 수 있으며, 다른 사용자는 ephemeral 거부를 받습니다. 승인 프롬프트에는 명령 텍스트가 포함되므로 신뢰할 수 있는 채널에서만 채널 전달을 활성화하세요. 세션 키에서 채널 ID를 파생할 수 없으면 OpenClaw는 DM 전달로 fallback합니다. - Discord는 다른 채팅 채널에서 사용하는 공유 승인 버튼도 렌더링합니다. 네이티브 Discord 어댑터는 주로 승인자 DM 라우팅과 채널 팬아웃을 추가합니다. - 해당 버튼이 있으면 이것이 기본 승인 UX입니다. OpenClaw는 - 도구 결과가 채팅 승인을 사용할 수 없다고 하거나 수동 승인이 유일한 경로라고 말할 때만 수동 `/approve` 명령을 포함해야 합니다. - Discord 네이티브 승인 런타임이 활성 상태가 아니면 OpenClaw는 - 로컬 결정적 `/approve ` 프롬프트를 계속 표시합니다. 런타임이 활성 상태이지만 네이티브 카드를 어떤 대상에도 전달할 수 없으면, - OpenClaw는 대기 중인 승인에서 정확한 `/approve` 명령이 포함된 동일 채팅 대체 알림을 보냅니다. + Discord는 다른 chat channel에서 사용하는 공유 승인 버튼도 렌더링합니다. native Discord adapter는 주로 승인자 DM routing과 channel fanout을 추가합니다. + 이러한 버튼이 있으면 기본 승인 UX가 됩니다. OpenClaw는 + tool 결과에서 chat approvals를 사용할 수 없거나 수동 승인이 유일한 경로라고 할 때만 수동 `/approve` 명령을 포함해야 합니다. + Discord native approval runtime이 활성 상태가 아니면 OpenClaw는 + local deterministic `/approve ` 프롬프트를 계속 표시합니다. runtime은 활성 상태지만 native card를 어떤 target에도 전달할 수 없으면 + OpenClaw는 pending approval의 정확한 `/approve` + 명령이 포함된 same-chat fallback notice를 보냅니다. - Gateway 인증과 승인 해석은 공유 Gateway 클라이언트 계약을 따릅니다(`plugin:` ID는 `plugin.approval.resolve`를 통해 해석되고, 다른 ID는 `exec.approval.resolve`를 통해 해석됨). 승인은 기본적으로 30분 후 만료됩니다. + Gateway auth와 승인 확인은 공유 Gateway client contract를 따릅니다(`plugin:` ID는 `plugin.approval.resolve`를 통해 확인되고, 다른 ID는 `exec.approval.resolve`를 통해 확인됨). 승인은 기본적으로 30분 후 만료됩니다. - [Exec 승인](/ko/tools/exec-approvals)을 참조하세요. + [Exec approvals](/ko/tools/exec-approvals)를 참조하세요. -## 도구와 작업 게이트 +## 도구 및 작업 게이트 -Discord 메시지 작업에는 메시징, 채널 관리, 중재, 상태, 메타데이터 작업이 포함됩니다. +Discord 메시지 작업에는 messaging, channel admin, moderation, presence, metadata 작업이 포함됩니다. 핵심 예시: -- 메시징: `sendMessage`, `readMessages`, `editMessage`, `deleteMessage`, `threadReply` -- 반응: `react`, `reactions`, `emojiList` -- 중재: `timeout`, `kick`, `ban` -- 상태: `setPresence` +- messaging: `sendMessage`, `readMessages`, `editMessage`, `deleteMessage`, `threadReply` +- reactions: `react`, `reactions`, `emojiList` +- moderation: `timeout`, `kick`, `ban` +- presence: `setPresence` -`event-create` 작업은 예약된 이벤트 커버 이미지를 설정하기 위해 선택적 `image` 매개변수(URL 또는 로컬 파일 경로)를 받습니다. +`event-create` 작업은 scheduled event cover image를 설정하기 위해 선택적 `image` 매개변수(URL 또는 local file path)를 허용합니다. -작업 게이트는 `channels.discord.actions.*` 아래에 있습니다. +Action gates는 `channels.discord.actions.*` 아래에 있습니다. 기본 게이트 동작: @@ -1091,10 +1111,10 @@ Discord 메시지 작업에는 메시징, 채널 관리, 중재, 상태, 메타 ## Components v2 UI -OpenClaw는 exec 승인과 교차 컨텍스트 마커에 Discord components v2를 사용합니다. Discord 메시지 작업도 사용자 지정 UI를 위해 `components`를 받을 수 있습니다(고급; discord 도구를 통해 컴포넌트 페이로드를 구성해야 함). 기존 `embeds`도 계속 사용할 수 있지만 권장하지 않습니다. +OpenClaw는 exec approvals와 cross-context markers에 Discord components v2를 사용합니다. Discord 메시지 작업은 custom UI를 위해 `components`도 허용할 수 있습니다(고급; discord tool을 통해 component payload를 구성해야 함). legacy `embeds`는 계속 사용할 수 있지만 권장되지 않습니다. -- `channels.discord.ui.components.accentColor`는 Discord 컴포넌트 컨테이너에서 사용하는 강조 색상(hex)을 설정합니다. -- `channels.discord.accounts..ui.components.accentColor`로 계정별로 설정합니다. +- `channels.discord.ui.components.accentColor`는 Discord component container에서 사용하는 accent color를 설정합니다(hex). +- `channels.discord.accounts..ui.components.accentColor`로 account별로 설정합니다. - components v2가 있으면 `embeds`는 무시됩니다. 예시: @@ -1115,20 +1135,20 @@ OpenClaw는 exec 승인과 교차 컨텍스트 마커에 Discord components v2 ## 음성 -Discord에는 두 가지 별개의 음성 표면이 있습니다. 실시간 **음성 채널**(연속 대화)과 **음성 메시지 첨부 파일**(파형 미리보기 형식)입니다. Gateway는 둘 다 지원합니다. +Discord에는 두 가지 별도의 음성 표면이 있습니다. realtime **voice channels**(연속 대화)와 **voice message attachments**(waveform preview 형식)입니다. Gateway는 둘 다 지원합니다. ### 음성 채널 설정 체크리스트: 1. Discord Developer Portal에서 Message Content Intent를 활성화합니다. -2. 역할/사용자 허용 목록을 사용하는 경우 Server Members Intent를 활성화합니다. -3. `bot` 및 `applications.commands` 범위로 봇을 초대합니다. -4. 대상 음성 채널에서 Connect, Speak, Send Messages, Read Message History 권한을 부여합니다. -5. 네이티브 명령(`commands.native` 또는 `channels.discord.commands.native`)을 활성화합니다. +2. role/user allowlists를 사용할 때 Server Members Intent를 활성화합니다. +3. `bot` 및 `applications.commands` scope로 bot을 초대합니다. +4. 대상 voice channel에서 Connect, Speak, Send Messages, Read Message History를 부여합니다. +5. native commands(`commands.native` 또는 `channels.discord.commands.native`)를 활성화합니다. 6. `channels.discord.voice`를 구성합니다. -세션을 제어하려면 `/vc join|leave|status`를 사용하세요. 이 명령은 계정 기본 에이전트를 사용하며 다른 Discord 명령과 동일한 허용 목록 및 그룹 정책 규칙을 따릅니다. +세션을 제어하려면 `/vc join|leave|status`를 사용하세요. 이 명령은 account default agent를 사용하며 다른 Discord 명령과 동일한 allowlist 및 group policy 규칙을 따릅니다. ```bash /vc join channel: @@ -1136,7 +1156,7 @@ Discord에는 두 가지 별개의 음성 표면이 있습니다. 실시간 ** /vc leave ``` -자동 참가 예시: +Auto-join 예시: ```json5 { @@ -1167,37 +1187,37 @@ Discord에는 두 가지 별개의 음성 표면이 있습니다. 실시간 ** 참고: -- `voice.tts`는 음성 재생에만 `messages.tts`를 재정의합니다. -- `voice.model`은 Discord 음성 채널 응답에 사용되는 LLM만 재정의합니다. 설정하지 않으면 라우팅된 에이전트 모델을 상속합니다. -- STT는 `tools.media.audio`를 사용합니다. `voice.model`은 전사에 영향을 주지 않습니다. -- 채널별 Discord `systemPrompt` 재정의는 해당 음성 채널의 음성 전사 턴에 적용됩니다. -- 음성 전사 턴은 Discord `allowFrom`(또는 `dm.allowFrom`)에서 소유자 상태를 파생합니다. 소유자가 아닌 발화자는 소유자 전용 도구(예: `gateway` 및 `cron`)에 접근할 수 없습니다. -- Discord 음성은 텍스트 전용 구성에서는 옵트인입니다. `/vc` 명령, 음성 런타임, `GuildVoiceStates` Gateway 인텐트를 활성화하려면 `channels.discord.voice.enabled=true`를 설정하거나 기존 `channels.discord.voice` 블록을 유지하세요. -- `channels.discord.intents.voiceStates`는 음성 상태 인텐트 구독을 명시적으로 재정의할 수 있습니다. 인텐트가 유효한 음성 활성화 상태를 따르게 하려면 설정하지 마세요. -- `voice.daveEncryption` 및 `voice.decryptionFailureTolerance`는 `@discordjs/voice` 참가 옵션으로 그대로 전달됩니다. +- `voice.tts`는 음성 재생에 대해서만 `messages.tts`를 재정의합니다. +- `voice.model`은 Discord voice channel 응답에 사용되는 LLM만 재정의합니다. routed agent model을 상속하려면 설정하지 않은 상태로 두세요. +- STT는 `tools.media.audio`를 사용합니다. `voice.model`은 transcription에 영향을 주지 않습니다. +- channel별 Discord `systemPrompt` 재정의는 해당 voice channel의 voice transcript turns에 적용됩니다. +- Voice transcript turns는 Discord `allowFrom`(또는 `dm.allowFrom`)에서 owner status를 파생합니다. non-owner speaker는 owner-only tools(예: `gateway` 및 `cron`)에 접근할 수 없습니다. +- Discord voice는 text-only config에서는 opt-in입니다. `/vc` 명령, voice runtime 및 `GuildVoiceStates` gateway intent를 활성화하려면 `channels.discord.voice.enabled=true`를 설정하세요(또는 기존 `channels.discord.voice` block을 유지하세요). +- `channels.discord.intents.voiceStates`는 voice-state intent subscription을 명시적으로 재정의할 수 있습니다. effective voice enablement를 따르게 하려면 설정하지 않은 상태로 두세요. +- `voice.daveEncryption` 및 `voice.decryptionFailureTolerance`는 `@discordjs/voice` join options로 전달됩니다. - `@discordjs/voice` 기본값은 설정하지 않은 경우 `daveEncryption=true` 및 `decryptionFailureTolerance=24`입니다. -- `voice.connectTimeoutMs`는 `/vc join` 및 자동 참가 시도의 초기 `@discordjs/voice` Ready 대기를 제어합니다. 기본값: `30000`. -- `voice.reconnectGraceMs`는 연결이 끊긴 음성 세션이 다시 연결을 시작하기 전에 OpenClaw가 얼마나 오래 기다린 뒤 세션을 제거할지 제어합니다. 기본값: `15000`. -- OpenClaw는 수신 복호화 실패도 감시하며 짧은 시간 동안 반복적으로 실패하면 음성 채널을 나갔다가 다시 참가하여 자동 복구합니다. -- 업데이트 후 수신 로그에 `DecryptionFailed(UnencryptedWhenPassthroughDisabled)`가 반복적으로 표시되면 의존성 보고서와 로그를 수집하세요. 번들된 `@discordjs/voice` 라인에는 discord.js 이슈 #11419를 닫은 discord.js PR #11449의 업스트림 패딩 수정이 포함되어 있습니다. +- `voice.connectTimeoutMs`는 `/vc join` 및 auto-join 시도의 초기 `@discordjs/voice` Ready 대기를 제어합니다. 기본값: `30000`. +- `voice.reconnectGraceMs`는 연결이 끊긴 voice session이 재연결을 시작하기 전까지 OpenClaw가 기다리는 시간을 제어하며, 이 시간이 지나면 세션을 종료합니다. 기본값: `15000`. +- OpenClaw는 receive decrypt failures도 감시하며, 짧은 시간 내 반복 실패가 발생하면 voice channel을 나갔다가 다시 참여하여 자동 복구합니다. +- 업데이트 후 receive logs에 `DecryptionFailed(UnencryptedWhenPassthroughDisabled)`가 반복적으로 표시되면 dependency report와 logs를 수집하세요. 번들된 `@discordjs/voice` line에는 discord.js issue #11419를 닫은 discord.js PR #11449의 upstream padding fix가 포함되어 있습니다. -음성 채널 파이프라인: +Voice channel pipeline: -- Discord PCM 캡처가 WAV 임시 파일로 변환됩니다. +- Discord PCM capture가 WAV temp file로 변환됩니다. - `tools.media.audio`가 STT를 처리합니다. 예: `openai/gpt-4o-mini-transcribe`. -- 전사는 Discord 인그레스와 라우팅을 통해 전송되며, 응답 LLM은 Discord 음성이 최종 TTS 재생을 소유하므로 에이전트 `tts` 도구를 숨기고 반환 텍스트를 요청하는 음성 출력 정책으로 실행됩니다. -- `voice.model`이 설정된 경우 이 음성 채널 턴의 응답 LLM만 재정의합니다. -- `voice.tts`는 `messages.tts` 위에 병합되며, 결과 오디오는 참가한 채널에서 재생됩니다. +- transcript는 Discord ingress 및 routing을 통해 전송되고, 응답 LLM은 agent `tts` tool을 숨기고 반환 텍스트를 요청하는 voice-output policy로 실행됩니다. 최종 TTS playback은 Discord voice가 소유하기 때문입니다. +- `voice.model`이 설정되면 이 voice-channel turn의 response LLM만 재정의합니다. +- `voice.tts`는 `messages.tts` 위에 병합되며, 결과 audio가 joined channel에서 재생됩니다. -자격 증명은 컴포넌트별로 해석됩니다. `voice.model`의 LLM 경로 인증, `tools.media.audio`의 STT 인증, `messages.tts`/`voice.tts`의 TTS 인증입니다. +Credentials는 component별로 확인됩니다. `voice.model`의 LLM route auth, `tools.media.audio`의 STT auth, `messages.tts`/`voice.tts`의 TTS auth입니다. ### 음성 메시지 -Discord 음성 메시지는 파형 미리보기를 표시하며 OGG/Opus 오디오가 필요합니다. OpenClaw는 파형을 자동으로 생성하지만, 검사와 변환을 위해 Gateway 호스트에 `ffmpeg`와 `ffprobe`가 필요합니다. +Discord voice messages는 waveform preview를 표시하며 OGG/Opus audio가 필요합니다. OpenClaw는 waveform을 자동으로 생성하지만, inspect 및 convert를 위해 gateway host에 `ffmpeg`와 `ffprobe`가 필요합니다. -- **로컬 파일 경로**를 제공하세요(URL은 거부됨). -- 텍스트 콘텐츠는 생략하세요(Discord는 같은 페이로드의 텍스트 + 음성 메시지를 거부함). -- 모든 오디오 형식을 사용할 수 있으며, OpenClaw가 필요에 따라 OGG/Opus로 변환합니다. +- **local file path**를 제공하세요(URL은 거부됨). +- text content를 생략하세요(Discord는 같은 payload에서 text + voice message를 거부함). +- 모든 audio format이 허용됩니다. OpenClaw는 필요에 따라 OGG/Opus로 변환합니다. ```bash message(action="send", channel="discord", target="channel:123", path="/path/to/audio.mp3", asVoice=true) @@ -1206,20 +1226,20 @@ message(action="send", channel="discord", target="channel:123", path="/path/to/a ## 문제 해결 - + - - Message Content Intent 활성화 - - 사용자/멤버 해석에 의존하는 경우 Server Members Intent 활성화 - - 인텐트를 변경한 뒤 gateway 재시작 + - Message Content Intent를 활성화합니다 + - user/member resolution에 의존하는 경우 Server Members Intent를 활성화합니다 + - intents를 변경한 후 gateway를 재시작합니다 - + - - `groupPolicy` 확인 - - `channels.discord.guilds` 아래의 길드 허용 목록 확인 - - 길드 `channels` 맵이 있으면 나열된 채널만 허용됨 - - `requireMention` 동작과 멘션 패턴 확인 + - `groupPolicy`를 확인합니다 + - `channels.discord.guilds` 아래의 guild allowlist를 확인합니다 + - guild `channels` map이 있으면 나열된 channels만 허용됩니다 + - `requireMention` 동작과 mention patterns를 확인합니다 유용한 확인: @@ -1231,29 +1251,29 @@ openclaw logs --follow - + 일반적인 원인: - - 일치하는 길드/채널 허용 목록 없이 `groupPolicy="allowlist"` 사용 - - `requireMention`이 잘못된 위치에 구성됨(`channels.discord.guilds` 또는 채널 항목 아래에 있어야 함) - - 발신자가 길드/채널 `users` 허용 목록에 의해 차단됨 + - 일치하는 guild/channel allowlist 없이 `groupPolicy="allowlist"`가 설정됨 + - `requireMention`이 잘못된 위치에 구성됨(`channels.discord.guilds` 또는 channel entry 아래에 있어야 함) + - sender가 guild/channel `users` allowlist에 의해 차단됨 - + - 일반적인 로그: + 일반적인 logs: - `Slow listener detected ...` - `stuck session: sessionKey=agent:...:discord:... state=processing ...` - Discord Gateway 큐 조정값: + Discord gateway queue knobs: - - 단일 계정: `channels.discord.eventQueue.listenerTimeout` - - 다중 계정: `channels.discord.accounts..eventQueue.listenerTimeout` - - 이는 Discord Gateway 리스너 작업만 제어하며, 에이전트 턴 수명은 제어하지 않음 + - single-account: `channels.discord.eventQueue.listenerTimeout` + - multi-account: `channels.discord.accounts..eventQueue.listenerTimeout` + - 이는 Discord gateway listener work만 제어하며 agent turn lifetime은 제어하지 않습니다 - Discord는 대기 중인 에이전트 턴에 채널 소유 타임아웃을 적용하지 않습니다. 메시지 리스너는 즉시 넘겨주며, 대기 중인 Discord 실행은 세션/도구/런타임 수명 주기가 완료되거나 작업을 중단할 때까지 세션별 순서를 유지합니다. + Discord는 queued agent turns에 channel-owned timeout을 적용하지 않습니다. Message listeners는 즉시 hand off하며, queued Discord runs는 session/tool/runtime lifecycle이 완료되거나 작업을 abort할 때까지 per-session ordering을 보존합니다. ```json5 { @@ -1273,45 +1293,45 @@ openclaw logs --follow - - OpenClaw는 연결하기 전에 Discord `/gateway/bot` 메타데이터를 가져옵니다. 일시적 실패는 Discord의 기본 Gateway URL로 대체되며 로그에서 속도 제한됩니다. + + OpenClaw는 연결하기 전에 Discord `/gateway/bot` 메타데이터를 가져옵니다. 일시적인 실패는 Discord의 기본 Gateway URL로 대체되며 로그에서 속도 제한됩니다. - 메타데이터 타임아웃 조정값: + 메타데이터 시간 초과 조정값: - 단일 계정: `channels.discord.gatewayInfoTimeoutMs` - 다중 계정: `channels.discord.accounts..gatewayInfoTimeoutMs` - - 구성이 설정되지 않았을 때 env 대체값: `OPENCLAW_DISCORD_GATEWAY_INFO_TIMEOUT_MS` - - 기본값: `30000`(30초), 최대값: `120000` + - 설정이 지정되지 않은 경우 env 대체값: `OPENCLAW_DISCORD_GATEWAY_INFO_TIMEOUT_MS` + - 기본값: `30000`(30초), 최대: `120000` - OpenClaw는 시작 중과 런타임 재연결 후 Discord의 gateway `READY` 이벤트를 기다립니다. 시작 시차를 둔 다중 계정 설정에는 기본값보다 더 긴 시작 READY 기간이 필요할 수 있습니다. + OpenClaw는 시작 중과 런타임 재연결 후 Discord의 Gateway `READY` 이벤트를 기다립니다. 시작 시차가 있는 다중 계정 설정은 기본값보다 더 긴 시작 READY 창이 필요할 수 있습니다. - READY 시간 초과 설정: + READY 시간 초과 조정값: - 시작 단일 계정: `channels.discord.gatewayReadyTimeoutMs` - 시작 다중 계정: `channels.discord.accounts..gatewayReadyTimeoutMs` - - 설정이 지정되지 않았을 때 시작 env 폴백: `OPENCLAW_DISCORD_READY_TIMEOUT_MS` + - 설정이 지정되지 않은 경우 시작 env 대체값: `OPENCLAW_DISCORD_READY_TIMEOUT_MS` - 시작 기본값: `15000`(15초), 최대: `120000` - 런타임 단일 계정: `channels.discord.gatewayRuntimeReadyTimeoutMs` - 런타임 다중 계정: `channels.discord.accounts..gatewayRuntimeReadyTimeoutMs` - - 설정이 지정되지 않았을 때 런타임 env 폴백: `OPENCLAW_DISCORD_RUNTIME_READY_TIMEOUT_MS` + - 설정이 지정되지 않은 경우 런타임 env 대체값: `OPENCLAW_DISCORD_RUNTIME_READY_TIMEOUT_MS` - 런타임 기본값: `30000`(30초), 최대: `120000` - `channels status --probe` 권한 검사는 숫자 채널 ID에서만 작동합니다. + `channels status --probe` 권한 검사는 숫자 채널 ID에 대해서만 작동합니다. - 슬러그 키를 사용하는 경우 런타임 매칭은 여전히 작동할 수 있지만, probe가 권한을 완전히 검증할 수는 없습니다. + 슬러그 키를 사용하는 경우에도 런타임 매칭은 계속 작동할 수 있지만, 프로브가 권한을 완전히 검증할 수는 없습니다. - - DM 비활성화됨: `channels.discord.dm.enabled=false` - - DM 정책 비활성화됨: `channels.discord.dmPolicy="disabled"`(레거시: `channels.discord.dm.policy`) + - DM 비활성화: `channels.discord.dm.enabled=false` + - DM 정책 비활성화: `channels.discord.dmPolicy="disabled"`(레거시: `channels.discord.dm.policy`) - `pairing` 모드에서 페어링 승인 대기 중 @@ -1319,8 +1339,8 @@ openclaw logs --follow 기본적으로 봇이 작성한 메시지는 무시됩니다. - `channels.discord.allowBots=true`를 설정하는 경우 루프 동작을 피하려면 엄격한 멘션 및 허용 목록 규칙을 사용하세요. - 봇을 멘션한 봇 메시지만 수락하려면 `channels.discord.allowBots="mentions"`를 선호하세요. + `channels.discord.allowBots=true`를 설정한 경우 루프 동작을 피하려면 엄격한 멘션 및 허용 목록 규칙을 사용하세요. + 봇을 멘션하는 봇 메시지만 허용하려면 `channels.discord.allowBots="mentions"`를 선호하세요. ```json5 { @@ -1349,13 +1369,13 @@ openclaw logs --follow - - Discord 음성 수신 복구 로직이 포함되도록 OpenClaw를 최신 상태로 유지하세요(`openclaw update`). - - `channels.discord.voice.daveEncryption=true`(기본값)인지 확인하세요. - - `channels.discord.voice.decryptionFailureTolerance=24`(업스트림 기본값)에서 시작하고 필요한 경우에만 조정하세요. - - 로그에서 다음을 확인하세요. + - Discord 음성 수신 복구 로직이 포함되도록 OpenClaw를 최신 상태로 유지하세요(`openclaw update`) + - `channels.discord.voice.daveEncryption=true`(기본값)인지 확인하세요 + - `channels.discord.voice.decryptionFailureTolerance=24`(업스트림 기본값)에서 시작하고 필요한 경우에만 조정하세요 + - 다음 로그를 확인하세요: - `discord voice: DAVE decrypt failures detected` - `discord voice: repeated decrypt failures; attempting rejoin` - - 자동 재참가 후에도 실패가 계속되면 로그를 수집하고 [discord.js #11419](https://github.com/discordjs/discord.js/issues/11419) 및 [discord.js #11449](https://github.com/discordjs/discord.js/pull/11449)의 업스트림 DAVE 수신 기록과 비교하세요. + - 자동 재참여 후에도 실패가 계속되면 로그를 수집하고 [discord.js #11419](https://github.com/discordjs/discord.js/issues/11419) 및 [discord.js #11449](https://github.com/discordjs/discord.js/pull/11449)의 업스트림 DAVE 수신 이력과 비교하세요 @@ -1364,19 +1384,19 @@ openclaw logs --follow 기본 참조: [설정 참조 - Discord](/ko/gateway/config-channels#discord). - + - 시작/인증: `enabled`, `token`, `accounts.*`, `allowBots` - 정책: `groupPolicy`, `dm.*`, `guilds.*`, `guilds.*.channels.*` - 명령: `commands.native`, `commands.useAccessGroups`, `configWrites`, `slashCommand.*` -- 이벤트 대기열: `eventQueue.listenerTimeout`(리스너 예산), `eventQueue.maxQueueSize`, `eventQueue.maxConcurrency` -- gateway: `gatewayInfoTimeoutMs`, `gatewayReadyTimeoutMs`, `gatewayRuntimeReadyTimeoutMs` -- 답장/기록: `replyToMode`, `historyLimit`, `dmHistoryLimit`, `dms.*.historyLimit` +- 이벤트 큐: `eventQueue.listenerTimeout`(리스너 예산), `eventQueue.maxQueueSize`, `eventQueue.maxConcurrency` +- Gateway: `gatewayInfoTimeoutMs`, `gatewayReadyTimeoutMs`, `gatewayRuntimeReadyTimeoutMs` +- 응답/기록: `replyToMode`, `historyLimit`, `dmHistoryLimit`, `dms.*.historyLimit` - 전달: `textChunkLimit`, `chunkMode`, `maxLinesPerMessage` - 스트리밍: `streaming`(레거시 별칭: `streamMode`), `streaming.preview.toolProgress`, `draftChunk`, `blockStreaming`, `blockStreamingCoalesce` -- 미디어/재시도: `mediaMaxMb`(아웃바운드 Discord 업로드 제한, 기본값 `100MB`), `retry` +- 미디어/재시도: `mediaMaxMb`(발신 Discord 업로드 제한, 기본값 `100MB`), `retry` - 작업: `actions.*` -- presence: `activity`, `status`, `activityType`, `activityUrl` +- 프레즌스: `activity`, `status`, `activityType`, `activityUrl` - UI: `ui.components.accentColor` - 기능: `threadBindings`, 최상위 `bindings[]`(`type: "acp"`), `pluralkit`, `execApprovals`, `intents`, `agentComponents`, `heartbeat`, `responsePrefix` @@ -1384,29 +1404,29 @@ openclaw logs --follow ## 안전 및 운영 -- 봇 토큰을 비밀로 취급하세요(감독 환경에서는 `DISCORD_BOT_TOKEN` 권장). +- 봇 토큰은 비밀로 취급하세요(감독 환경에서는 `DISCORD_BOT_TOKEN` 권장). - 최소 권한 Discord 권한을 부여하세요. -- 명령 배포/상태가 오래된 경우 Gateway를 다시 시작하고 `openclaw channels status --probe`로 다시 확인하세요. +- 명령 배포/상태가 오래된 경우 Gateway를 재시작하고 `openclaw channels status --probe`로 다시 확인하세요. ## 관련 항목 - Discord 사용자를 gateway에 페어링합니다. + Discord 사용자를 Gateway에 페어링합니다. - 그룹 채팅 및 허용 목록 동작. + 그룹 채팅 및 허용 목록 동작입니다. - 수신 메시지를 에이전트로 라우팅합니다. + 인바운드 메시지를 에이전트로 라우팅합니다. - 위협 모델 및 강화. + 위협 모델 및 강화입니다. 길드와 채널을 에이전트에 매핑합니다. - 네이티브 명령 동작. + 네이티브 명령 동작입니다. diff --git a/docs/ko/channels/slack.md b/docs/ko/channels/slack.md index 3ab830152..0f4546214 100644 --- a/docs/ko/channels/slack.md +++ b/docs/ko/channels/slack.md @@ -4,24 +4,24 @@ read_when: summary: Slack 설정 및 런타임 동작(Socket Mode + HTTP 요청 URL) title: Slack x-i18n: - generated_at: "2026-05-04T02:22:03Z" + generated_at: "2026-05-04T07:02:41Z" model: gpt-5.5 provider: openai - source_hash: 2be45f03511a64373b1f4316c59800eeeef8baccb4c00454b49999258b2e546b + source_hash: d4a91fc1ae5f1e03f714308be54e164ef204809e74efabed8dc75c3035c14228 source_path: channels/slack.md workflow: 16 --- -DM 및 채널에서 Slack 앱 통합을 통해 프로덕션 준비 완료 상태로 사용할 수 있습니다. 기본 모드는 Socket Mode이며, HTTP Request URLs도 지원됩니다. +Slack 앱 통합을 통해 DM 및 채널에서 프로덕션 준비 상태로 사용할 수 있습니다. 기본 모드는 Socket Mode이며, HTTP Request URLs도 지원됩니다. - + Slack DM은 기본적으로 페어링 모드를 사용합니다. - - 네이티브 명령어 동작 및 명령어 카탈로그입니다. + + 네이티브 명령 동작 및 명령 카탈로그입니다. - + 채널 간 진단 및 복구 플레이북입니다. @@ -29,19 +29,19 @@ DM 및 채널에서 Slack 앱 통합을 통해 프로덕션 준비 완료 상태 ## 빠른 설정 - + - + Slack 앱 설정에서 **[Create New App](https://api.slack.com/apps/new)** 버튼을 누릅니다. - - **from a manifest**를 선택하고 앱의 워크스페이스를 선택합니다. - - 아래의 [예시 매니페스트](#manifest-and-scope-checklist)를 붙여넣고 계속 진행하여 만듭니다. - - `connections:write`가 있는 **App-Level Token**(`xapp-...`)을 생성합니다. - - 앱을 설치하고 표시되는 **Bot Token**(`xoxb-...`)을 복사합니다. + - **from a manifest**를 선택하고 앱용 워크스페이스를 선택합니다 + - 아래의 [예제 매니페스트](#manifest-and-scope-checklist)를 붙여넣고 생성을 계속합니다 + - `connections:write`가 포함된 **App-Level Token**(`xapp-...`)을 생성합니다 + - 앱을 설치하고 표시된 **Bot Token**(`xoxb-...`)을 복사합니다 - + 권장 SecretRef 설정: @@ -73,7 +73,7 @@ SLACK_BOT_TOKEN=xoxb-... - + ```bash openclaw gateway @@ -86,17 +86,17 @@ openclaw gateway - + Slack 앱 설정에서 **[Create New App](https://api.slack.com/apps/new)** 버튼을 누릅니다. - - **from a manifest**를 선택하고 앱의 워크스페이스를 선택합니다. - - [예시 매니페스트](#manifest-and-scope-checklist)를 붙여넣고 만들기 전에 URL을 업데이트합니다. - - 요청 검증을 위해 **Signing Secret**을 저장합니다. - - 앱을 설치하고 표시되는 **Bot Token**(`xoxb-...`)을 복사합니다. + - **from a manifest**를 선택하고 앱용 워크스페이스를 선택합니다 + - [예제 매니페스트](#manifest-and-scope-checklist)를 붙여넣고 생성 전에 URL을 업데이트합니다 + - 요청 검증을 위해 **Signing Secret**을 저장합니다 + - 앱을 설치하고 표시된 **Bot Token**(`xoxb-...`)을 복사합니다 - + 권장 SecretRef 설정: @@ -121,14 +121,14 @@ openclaw config patch --file ./slack.http.patch.json5 ``` - 다중 계정 HTTP에는 고유한 webhook 경로를 사용하세요. + 다중 계정 HTTP에는 고유한 webhook 경로를 사용하세요 - 등록이 충돌하지 않도록 각 계정에 별도의 `webhookPath`(기본값 `/slack/events`)를 지정하세요. + 등록이 충돌하지 않도록 각 계정에 고유한 `webhookPath`(기본값 `/slack/events`)를 지정하세요. - + ```bash openclaw gateway @@ -142,7 +142,7 @@ openclaw gateway ## Socket Mode 전송 튜닝 -OpenClaw는 Socket Mode에 대해 Slack SDK 클라이언트 pong 제한 시간을 기본적으로 15초로 설정합니다. 워크스페이스 또는 호스트별 튜닝이 필요한 경우에만 전송 설정을 재정의하세요. +OpenClaw는 Socket Mode에서 Slack SDK 클라이언트 pong 타임아웃을 기본적으로 15초로 설정합니다. 워크스페이스 또는 호스트별 튜닝이 필요할 때만 전송 설정을 재정의하세요. ```json5 { @@ -159,11 +159,11 @@ OpenClaw는 Socket Mode에 대해 Slack SDK 클라이언트 pong 제한 시간 } ``` -Slack websocket pong/server-ping 제한 시간이 로그에 기록되는 Socket Mode 워크스페이스 또는 이벤트 루프 고갈이 알려진 호스트에서만 이 설정을 사용하세요. `clientPingTimeout`은 SDK가 클라이언트 ping을 보낸 후 pong을 기다리는 시간이고, `serverPingTimeout`은 Slack 서버 ping을 기다리는 시간입니다. 앱 메시지와 이벤트는 전송 활성 신호가 아니라 애플리케이션 상태로 유지됩니다. +Slack websocket pong/server-ping 타임아웃을 기록하는 Socket Mode 워크스페이스 또는 이벤트 루프 고갈이 알려진 호스트에서 실행되는 경우에만 이것을 사용하세요. `clientPingTimeout`은 SDK가 클라이언트 ping을 보낸 뒤 pong을 기다리는 시간이고, `serverPingTimeout`은 Slack 서버 ping을 기다리는 시간입니다. 앱 메시지와 이벤트는 전송 활성 신호가 아니라 애플리케이션 상태로 유지됩니다. -## 매니페스트 및 범위 체크리스트 +## 매니페스트 및 scope 체크리스트 -기본 Slack 앱 매니페스트는 Socket Mode와 HTTP Request URLs에서 동일합니다. `settings` 블록(및 슬래시 명령어 `url`)만 다릅니다. +기본 Slack 앱 매니페스트는 Socket Mode와 HTTP Request URLs에서 동일합니다. `settings` 블록(및 slash command `url`)만 다릅니다. 기본 매니페스트(Socket Mode 기본값): @@ -240,7 +240,7 @@ Slack websocket pong/server-ping 제한 시간이 로그에 기록되는 Socket } ``` -**HTTP Request URLs 모드**의 경우 `settings`를 HTTP 변형으로 바꾸고 각 슬래시 명령어에 `url`을 추가합니다. 공개 URL이 필요합니다. +**HTTP Request URLs 모드**의 경우 `settings`를 HTTP 변형으로 교체하고 각 slash command에 `url`을 추가합니다. 공개 URL이 필요합니다. ```json { @@ -286,20 +286,20 @@ Slack websocket pong/server-ping 제한 시간이 로그에 기록되는 Socket 위 기본값을 확장하는 다양한 기능을 노출합니다. -기본 매니페스트는 Slack App Home **Home** 탭을 활성화하고 `app_home_opened`를 구독합니다. 워크스페이스 멤버가 Home 탭을 열면 OpenClaw는 `views.publish`로 안전한 기본 Home 보기를 게시합니다. 대화 페이로드나 비공개 구성은 포함되지 않습니다. Slack DM을 위해 **Messages** 탭은 계속 활성화되어 있습니다. +기본 매니페스트는 Slack App Home **Home** 탭을 활성화하고 `app_home_opened`를 구독합니다. 워크스페이스 멤버가 Home 탭을 열면 OpenClaw는 `views.publish`로 안전한 기본 Home 보기를 게시합니다. 대화 페이로드나 비공개 구성은 포함되지 않습니다. **Messages** 탭은 Slack DM용으로 계속 활성화됩니다. - + - 단일 구성 명령어 대신 여러 [네이티브 슬래시 명령어](#commands-and-slash-behavior)를 세부적으로 사용할 수 있습니다. + 여러 [네이티브 slash command](#commands-and-slash-behavior)를 미묘한 차이가 있는 단일 구성 명령 대신 사용할 수 있습니다. - - `/status` 명령어는 예약되어 있으므로 `/status` 대신 `/agentstatus`를 사용하세요. - - 한 번에 25개를 초과하는 슬래시 명령어를 사용할 수 없습니다. + - `/status` 명령은 예약되어 있으므로 `/status` 대신 `/agentstatus`를 사용하세요. + - 한 번에 25개를 초과하는 slash command를 사용할 수는 없습니다. - 기존 `features.slash_commands` 섹션을 [사용 가능한 명령어](/ko/tools/slash-commands#command-list)의 일부로 바꾸세요. + 기존 `features.slash_commands` 섹션을 [사용 가능한 명령](/ko/tools/slash-commands#command-list)의 하위 집합으로 교체하세요. - + ```json { @@ -423,7 +423,7 @@ Slack websocket pong/server-ping 제한 시간이 로그에 기록되는 Socket - 위 Socket Mode와 동일한 `slash_commands` 목록을 사용하고, 모든 항목에 `"url": "https://gateway-host.example.com/slack/events"`를 추가하세요. 예시: + 위 Socket Mode와 동일한 `slash_commands` 목록을 사용하고 모든 항목에 `"url": "https://gateway-host.example.com/slack/events"`를 추가하세요. 예: ```json { @@ -443,14 +443,14 @@ Slack websocket pong/server-ping 제한 시간이 로그에 기록되는 Socket } ``` - 목록의 모든 명령어에서 해당 `url` 값을 반복하세요. + 목록의 모든 명령에 해당 `url` 값을 반복해서 넣으세요. - - 발신 메시지가 기본 Slack 앱 ID 대신 활성 에이전트 ID(사용자 지정 사용자 이름 및 아이콘)를 사용하게 하려면 `chat:write.customize` 봇 범위를 추가하세요. + + 발신 메시지에서 기본 Slack 앱 ID 대신 활성 에이전트 ID(사용자 지정 사용자 이름 및 아이콘)를 사용하려면 `chat:write.customize` 봇 범위를 추가하세요. 이모지 아이콘을 사용하는 경우 Slack은 `:emoji_name:` 구문을 기대합니다. @@ -475,22 +475,22 @@ Slack websocket pong/server-ping 제한 시간이 로그에 기록되는 Socket - HTTP 모드에는 `botToken` + `signingSecret`이 필요합니다. - `botToken`, `appToken`, `signingSecret`, `userToken`은 일반 텍스트 문자열 또는 SecretRef 객체를 허용합니다. -- 구성 토큰은 env fallback을 재정의합니다. -- `SLACK_BOT_TOKEN` / `SLACK_APP_TOKEN` env fallback은 기본 계정에만 적용됩니다. -- `userToken`(`xoxp-...`)은 구성 전용(env fallback 없음)이며 기본값은 읽기 전용 동작(`userTokenReadOnly: true`)입니다. +- 구성 토큰은 env 폴백을 재정의합니다. +- `SLACK_BOT_TOKEN` / `SLACK_APP_TOKEN` env 폴백은 기본 계정에만 적용됩니다. +- `userToken`(`xoxp-...`)은 구성 전용(env 폴백 없음)이며 기본값은 읽기 전용 동작(`userTokenReadOnly: true`)입니다. 상태 스냅샷 동작: - Slack 계정 검사는 자격 증명별 `*Source` 및 `*Status` 필드(`botToken`, `appToken`, `signingSecret`, `userToken`)를 추적합니다. -- 상태는 `available`, `configured_unavailable`, `missing` 중 하나입니다. -- `configured_unavailable`은 계정이 SecretRef 또는 다른 비인라인 시크릿 소스를 통해 - 구성되었지만, 현재 명령/런타임 경로에서 실제 값을 확인할 수 없었음을 의미합니다. -- HTTP 모드에서는 `signingSecretStatus`가 포함되고, Socket Mode에서는 +- 상태는 `available`, `configured_unavailable` 또는 `missing`입니다. +- `configured_unavailable`은 계정이 SecretRef 또는 다른 비인라인 비밀 소스를 통해 + 구성되어 있지만, 현재 명령/런타임 경로에서 실제 값을 확인할 수 없다는 뜻입니다. +- HTTP 모드에서는 `signingSecretStatus`가 포함되며, Socket Mode에서는 필요한 쌍이 `botTokenStatus` + `appTokenStatus`입니다. -작업/디렉터리 읽기에는 구성된 경우 사용자 토큰이 선호될 수 있습니다. 쓰기에는 봇 토큰이 계속 선호됩니다. 사용자 토큰 쓰기는 `userTokenReadOnly: false`이고 봇 토큰을 사용할 수 없을 때만 허용됩니다. +작업/디렉터리 읽기의 경우 구성되어 있으면 사용자 토큰이 우선될 수 있습니다. 쓰기의 경우 봇 토큰이 계속 우선됩니다. 사용자 토큰 쓰기는 `userTokenReadOnly: false`이고 봇 토큰을 사용할 수 없을 때만 허용됩니다. ## 작업 및 게이트 @@ -501,13 +501,13 @@ Slack 작업은 `channels.slack.actions.*`로 제어됩니다. | 그룹 | 기본값 | | ---------- | ------- | -| messages | 활성화됨 | -| reactions | 활성화됨 | -| pins | 활성화됨 | -| memberInfo | 활성화됨 | -| emojiList | 활성화됨 | +| 메시지 | 활성화됨 | +| 반응 | 활성화됨 | +| 핀 | 활성화됨 | +| 멤버 정보 | 활성화됨 | +| 이모지 목록 | 활성화됨 | -현재 Slack 메시지 작업에는 `send`, `upload-file`, `download-file`, `read`, `edit`, `delete`, `pin`, `unpin`, `list-pins`, `member-info`, `emoji-list`가 포함됩니다. `download-file`은 인바운드 파일 플레이스홀더에 표시된 Slack 파일 ID를 허용하며, 이미지의 경우 이미지 미리 보기를, 다른 파일 형식의 경우 로컬 파일 메타데이터를 반환합니다. +현재 Slack 메시지 작업에는 `send`, `upload-file`, `download-file`, `read`, `edit`, `delete`, `pin`, `unpin`, `list-pins`, `member-info`, `emoji-list`가 포함됩니다. `download-file`은 수신 파일 자리 표시자에 표시된 Slack 파일 ID를 허용하고, 이미지의 경우 이미지 미리 보기를 반환하며 다른 파일 형식의 경우 로컬 파일 메타데이터를 반환합니다. ## 접근 제어 및 라우팅 @@ -517,7 +517,7 @@ Slack 작업은 `channels.slack.actions.*`로 제어됩니다. - `pairing`(기본값) - `allowlist` - - `open`(`channels.slack.allowFrom`에 `"*"`가 포함되어야 함) + - `open`(`channels.slack.allowFrom`에 `"*"`를 포함해야 함) - `disabled` DM 플래그: @@ -534,7 +534,7 @@ Slack 작업은 `channels.slack.actions.*`로 제어됩니다. - 명명된 계정은 자체 `allowFrom`이 설정되지 않은 경우 `channels.slack.allowFrom`을 상속합니다. - 명명된 계정은 `channels.slack.accounts.default.allowFrom`을 상속하지 않습니다. - 레거시 `channels.slack.dm.policy` 및 `channels.slack.dm.allowFrom`은 호환성을 위해 계속 읽힙니다. `openclaw doctor --fix`는 접근을 변경하지 않고 가능할 때 이를 `dmPolicy`와 `allowFrom`으로 마이그레이션합니다. + 레거시 `channels.slack.dm.policy` 및 `channels.slack.dm.allowFrom`은 호환성을 위해 계속 읽습니다. 접근을 변경하지 않고 처리할 수 있으면 `openclaw doctor --fix`가 이를 `dmPolicy`와 `allowFrom`으로 마이그레이션합니다. DM에서의 페어링은 `openclaw pairing approve slack `를 사용합니다. @@ -549,16 +549,16 @@ Slack 작업은 `channels.slack.actions.*`로 제어됩니다. 채널 허용 목록은 `channels.slack.channels` 아래에 있으며 구성 키로 **안정적인 Slack 채널 ID**(예: `C12345678`)를 사용해야 합니다. - 런타임 참고: `channels.slack`이 완전히 없으면(env 전용 설정), 런타임은 `groupPolicy="allowlist"`로 fallback하고 경고를 기록합니다(`channels.defaults.groupPolicy`가 설정되어 있더라도). + 런타임 참고: `channels.slack`이 완전히 누락된 경우(env 전용 설정), 런타임은 `groupPolicy="allowlist"`로 폴백하고 경고를 기록합니다(`channels.defaults.groupPolicy`가 설정되어 있어도 동일). 이름/ID 확인: - - 채널 허용 목록 항목과 DM 허용 목록 항목은 토큰 접근이 허용할 때 시작 시 확인됩니다 - - 확인되지 않은 채널 이름 항목은 구성된 상태로 유지되지만 기본적으로 라우팅에서는 무시됩니다 - - 인바운드 권한 부여와 채널 라우팅은 기본적으로 ID 우선입니다. 직접 사용자 이름/슬러그 매칭에는 `channels.slack.dangerouslyAllowNameMatching: true`가 필요합니다 + - 채널 허용 목록 항목과 DM 허용 목록 항목은 토큰 접근이 허용될 때 시작 시 확인됩니다. + - 확인되지 않은 채널 이름 항목은 구성된 상태로 유지되지만 기본적으로 라우팅에서는 무시됩니다. + - 수신 권한 부여와 채널 라우팅은 기본적으로 ID 우선입니다. 직접 사용자 이름/슬러그 매칭에는 `channels.slack.dangerouslyAllowNameMatching: true`가 필요합니다. - 이름 기반 키(`#channel-name` 또는 `channel-name`)는 `groupPolicy: "allowlist"`에서 일치하지 않습니다. 채널 조회는 기본적으로 ID 우선이므로 이름 기반 키는 절대 성공적으로 라우팅되지 않으며 해당 채널의 모든 메시지가 조용히 차단됩니다. 이는 채널 키가 라우팅에 필요하지 않아 이름 기반 키가 작동하는 것처럼 보이는 `groupPolicy: "open"`과 다릅니다. + 이름 기반 키(`#channel-name` 또는 `channel-name`)는 `groupPolicy: "allowlist"`에서 일치하지 **않습니다**. 채널 조회는 기본적으로 ID 우선이므로 이름 기반 키는 절대 성공적으로 라우팅되지 않으며 해당 채널의 모든 메시지는 조용히 차단됩니다. 이는 채널 키가 라우팅에 필요하지 않아 이름 기반 키가 작동하는 것처럼 보이는 `groupPolicy: "open"`과 다릅니다. 항상 Slack 채널 ID를 키로 사용하세요. 찾는 방법: Slack에서 채널을 마우스 오른쪽 버튼으로 클릭 → **링크 복사** — ID(`C...`)가 URL 끝에 표시됩니다. @@ -601,11 +601,11 @@ Slack 작업은 `channels.slack.actions.*`로 제어됩니다. 멘션 소스: - 명시적 앱 멘션(`<@botId>`) - - 봇 사용자가 해당 사용자 그룹의 멤버일 때 Slack 사용자 그룹 멘션(``), `usergroups:read` 필요 - - 멘션 정규식 패턴(`agents.list[].groupChat.mentionPatterns`, fallback `messages.groupChat.mentionPatterns`) - - 암시적 봇 답장 스레드 동작(`thread.requireExplicitMention`이 `true`이면 비활성화됨) + - 봇 사용자가 해당 사용자 그룹의 멤버일 때 Slack 사용자 그룹 멘션(``); `usergroups:read` 필요 + - 멘션 정규식 패턴(`agents.list[].groupChat.mentionPatterns`, 대체 `messages.groupChat.mentionPatterns`) + - 암시적 봇 답글 스레드 동작(`thread.requireExplicitMention`이 `true`일 때 비활성화됨) - 채널별 제어(`channels.slack.channels.`, 이름은 시작 시 확인 또는 `dangerouslyAllowNameMatching`을 통해서만 가능): + 채널별 제어(`channels.slack.channels.`; 이름은 시작 시 해석 또는 `dangerouslyAllowNameMatching`을 통해서만 사용): - `requireMention` - `users`(허용 목록) @@ -613,54 +613,54 @@ Slack 작업은 `channels.slack.actions.*`로 제어됩니다. - `skills` - `systemPrompt` - `tools`, `toolsBySender` - - `toolsBySender` 키 형식: `id:`, `e164:`, `username:`, `name:`, 또는 `"*"` 와일드카드 + - `toolsBySender` 키 형식: `id:`, `e164:`, `username:`, `name:` 또는 `"*"` 와일드카드 (레거시 접두사 없는 키는 여전히 `id:`에만 매핑됨) - `allowBots`는 채널과 비공개 채널에 대해 보수적으로 동작합니다. 봇이 작성한 룸 메시지는 보내는 봇이 해당 룸의 `users` 허용 목록에 명시적으로 나열되어 있거나, `channels.slack.allowFrom`의 명시적 Slack 소유자 ID 중 하나 이상이 현재 룸 멤버일 때만 허용됩니다. 와일드카드와 표시 이름 소유자 항목은 소유자 존재 조건을 충족하지 않습니다. 소유자 존재 여부는 Slack `conversations.members`를 사용합니다. 앱에 룸 유형에 맞는 읽기 범위가 있는지 확인하세요(공개 채널은 `channels:read`, 비공개 채널은 `groups:read`). 멤버 조회가 실패하면 OpenClaw는 봇이 작성한 룸 메시지를 드롭합니다. + `allowBots`는 채널 및 비공개 채널에 대해 보수적으로 동작합니다. 봇이 작성한 방 메시지는 보내는 봇이 해당 방의 `users` 허용 목록에 명시적으로 나열되어 있거나, `channels.slack.allowFrom`의 명시적 Slack 소유자 ID 중 하나 이상이 현재 방 멤버일 때만 허용됩니다. 와일드카드와 표시 이름 소유자 항목은 소유자 존재 조건을 충족하지 않습니다. 소유자 존재 여부는 Slack `conversations.members`를 사용합니다. 앱에 방 유형에 맞는 읽기 범위(공개 채널은 `channels:read`, 비공개 채널은 `groups:read`)가 있는지 확인하세요. 멤버 조회에 실패하면 OpenClaw는 봇이 작성한 방 메시지를 폐기합니다. -## 스레딩, 세션 및 답장 태그 +## 스레딩, 세션, 답글 태그 - DM은 `direct`로, 채널은 `channel`로, MPIM은 `group`으로 라우팅됩니다. - Slack 라우트 바인딩은 원시 피어 ID와 `channel:C12345678`, `user:U12345678`, `<@U12345678>` 같은 Slack 대상 형식을 허용합니다. -- 기본 `session.dmScope=main`에서는 Slack DM이 에이전트 메인 세션으로 병합됩니다. +- 기본 `session.dmScope=main`에서는 Slack DM이 에이전트 메인 세션으로 합쳐집니다. - 채널 세션: `agent::slack:channel:`. -- 스레드 답장은 적용 가능한 경우 스레드 세션 접미사(`:thread:`)를 만들 수 있습니다. -- `channels.slack.thread.historyScope` 기본값은 `thread`이고, `thread.inheritParent` 기본값은 `false`입니다. -- `channels.slack.thread.initialHistoryLimit`은 새 스레드 세션이 시작될 때 가져올 기존 스레드 메시지 수를 제어합니다(기본값 `20`, 비활성화하려면 `0` 설정). -- `channels.slack.thread.requireExplicitMention`(기본값 `false`): `true`이면 암시적 스레드 멘션을 억제하여 봇이 이미 해당 스레드에 참여했더라도 스레드 안의 명시적 `@bot` 멘션에만 응답합니다. 이것이 없으면 봇이 참여한 스레드의 답장이 `requireMention` 게이트를 우회합니다. +- 스레드 답글은 적용 가능한 경우 스레드 세션 접미사(`:thread:`)를 만들 수 있습니다. +- `channels.slack.thread.historyScope` 기본값은 `thread`입니다. `thread.inheritParent` 기본값은 `false`입니다. +- `channels.slack.thread.initialHistoryLimit`는 새 스레드 세션이 시작될 때 가져올 기존 스레드 메시지 수를 제어합니다(기본값 `20`; 비활성화하려면 `0`으로 설정). +- `channels.slack.thread.requireExplicitMention`(기본값 `false`): `true`이면 암시적 스레드 멘션을 억제하여, 봇이 이미 스레드에 참여했더라도 스레드 안의 명시적 `@bot` 멘션에만 봇이 응답합니다. 이것이 없으면 봇이 참여한 스레드의 답글은 `requireMention` 게이트를 우회합니다. -답장 스레딩 제어: +답글 스레딩 제어: - `channels.slack.replyToMode`: `off|first|all|batched`(기본값 `off`) - `channels.slack.replyToModeByChatType`: `direct|group|channel`별 설정 -- 직접 채팅을 위한 레거시 fallback: `channels.slack.dm.replyToMode` +- 직접 채팅용 레거시 대체값: `channels.slack.dm.replyToMode` -수동 답장 태그가 지원됩니다. +수동 답글 태그가 지원됩니다. - `[[reply_to_current]]` - `[[reply_to:]]` -`replyToMode="off"`는 명시적 `[[reply_to_*]]` 태그를 포함해 Slack의 **모든** 답장 스레딩을 비활성화합니다. 이는 `"off"` 모드에서도 명시적 태그가 계속 적용되는 Telegram과 다릅니다. Slack 스레드는 채널에서 메시지를 숨기지만 Telegram 답장은 인라인으로 계속 표시됩니다. +`replyToMode="off"`는 명시적 `[[reply_to_*]]` 태그를 포함해 Slack의 **모든** 답글 스레딩을 비활성화합니다. 이는 `"off"` 모드에서도 명시적 태그가 계속 적용되는 Telegram과 다릅니다. Slack 스레드는 채널에서 메시지를 숨기지만, Telegram 답글은 인라인으로 계속 표시됩니다. ## 확인 반응 `ackReaction`은 OpenClaw가 인바운드 메시지를 처리하는 동안 확인 이모지를 보냅니다. -확인 순서: +해석 순서: - `channels.slack.accounts..ackReaction` - `channels.slack.ackReaction` - `messages.ackReaction` -- 에이전트 ID 이모지 fallback(`agents.list[].identity.emoji`, 없으면 "👀") +- 에이전트 ID 이모지 대체값(`agents.list[].identity.emoji`, 없으면 "👀") 참고: -- Slack은 shortcode를 기대합니다(예: `"eyes"`). +- Slack은 숏코드를 기대합니다(예: `"eyes"`). - Slack 계정 또는 전역에서 반응을 비활성화하려면 `""`를 사용하세요. ## 텍스트 스트리밍 @@ -669,20 +669,39 @@ Slack 작업은 `channels.slack.actions.*`로 제어됩니다. - `off`: 실시간 미리 보기 스트리밍을 비활성화합니다. - `partial`(기본값): 미리 보기 텍스트를 최신 부분 출력으로 교체합니다. -- `block`: 청크 단위 미리 보기 업데이트를 추가합니다. -- `progress`: 생성 중 진행 상태 텍스트를 표시한 다음 최종 텍스트를 보냅니다. -- `streaming.preview.toolProgress`: 초안 미리 보기가 활성 상태일 때 도구/진행 업데이트를 동일한 편집된 미리 보기 메시지로 라우팅합니다(기본값: `true`). 별도의 도구/진행 메시지를 유지하려면 `false`로 설정하세요. +- `block`: 청크 미리 보기 업데이트를 추가합니다. +- `progress`: 생성 중에는 진행 상태 텍스트를 표시한 뒤 최종 텍스트를 보냅니다. +- `streaming.preview.toolProgress`: 초안 미리 보기가 활성화되어 있을 때 도구/진행 업데이트를 같은 편집된 미리 보기 메시지로 라우팅합니다(기본값: `true`). 별도의 도구/진행 메시지를 유지하려면 `false`로 설정하세요. +- `streaming.preview.commandText` / `streaming.progress.commandText`: 원시 명령/실행 텍스트를 숨기면서 간결한 도구 진행 줄을 유지하려면 `status`로 설정합니다(기본값: `raw`). + +간결한 진행 줄은 유지하면서 원시 명령/실행 텍스트 숨기기: + +```json +{ + "channels": { + "slack": { + "streaming": { + "mode": "progress", + "progress": { + "toolProgress": true, + "commandText": "status" + } + } + } + } +} +``` `channels.slack.streaming.nativeTransport`는 `channels.slack.streaming.mode`가 `partial`일 때 Slack 네이티브 텍스트 스트리밍을 제어합니다(기본값: `true`). -- 네이티브 텍스트 스트리밍과 Slack 어시스턴트 스레드 상태가 표시되려면 답장 스레드를 사용할 수 있어야 합니다. 스레드 선택은 계속 `replyToMode`를 따릅니다. -- 채널, 그룹 채팅, 최상위 DM 루트는 네이티브 스트리밍을 사용할 수 없거나 답장 스레드가 없는 경우에도 일반 초안 미리 보기를 사용할 수 있습니다. -- 최상위 Slack DM은 기본적으로 스레드 밖에 유지되므로 Slack의 스레드 스타일 네이티브 스트림/상태 미리 보기를 표시하지 않습니다. 대신 OpenClaw는 DM에 초안 미리 보기를 게시하고 편집합니다. -- 미디어 및 비텍스트 페이로드는 일반 전달로 fallback합니다. -- 미디어/오류 최종 응답은 대기 중인 미리 보기 편집을 취소합니다. 적격 텍스트/블록 최종 응답은 미리 보기를 제자리에서 편집할 수 있을 때만 플러시됩니다. -- 스트리밍이 답장 중간에 실패하면 OpenClaw는 남은 페이로드에 대해 일반 전달로 fallback합니다. +- 네이티브 텍스트 스트리밍과 Slack 어시스턴트 스레드 상태가 표시되려면 답글 스레드가 있어야 합니다. 스레드 선택은 계속 `replyToMode`를 따릅니다. +- 채널, 그룹 채팅, 최상위 DM 루트는 네이티브 스트리밍을 사용할 수 없거나 답글 스레드가 없을 때도 일반 초안 미리 보기를 사용할 수 있습니다. +- 최상위 Slack DM은 기본적으로 스레드 밖에 유지되므로 Slack의 스레드 스타일 네이티브 스트림/상태 미리 보기를 표시하지 않습니다. 대신 OpenClaw가 DM에 초안 미리 보기를 게시하고 편집합니다. +- 미디어 및 비텍스트 페이로드는 일반 전달로 대체됩니다. +- 미디어/오류 최종 응답은 대기 중인 미리 보기 편집을 취소합니다. 조건에 맞는 텍스트/블록 최종 응답은 미리 보기를 제자리에서 편집할 수 있을 때만 플러시됩니다. +- 스트리밍이 답글 도중 실패하면 OpenClaw는 남은 페이로드에 대해 일반 전달로 대체합니다. -Slack 네이티브 텍스트 스트리밍 대신 초안 미리 보기를 사용합니다. +Slack 네이티브 텍스트 스트리밍 대신 초안 미리 보기 사용: ```json5 { @@ -700,45 +719,45 @@ Slack 네이티브 텍스트 스트리밍 대신 초안 미리 보기를 사용 레거시 키: - `channels.slack.streamMode`(`replace | status_final | append`)는 `channels.slack.streaming.mode`로 자동 마이그레이션됩니다. -- boolean `channels.slack.streaming`은 `channels.slack.streaming.mode`와 `channels.slack.streaming.nativeTransport`로 자동 마이그레이션됩니다. +- 불리언 `channels.slack.streaming`은 `channels.slack.streaming.mode` 및 `channels.slack.streaming.nativeTransport`로 자동 마이그레이션됩니다. - 레거시 `channels.slack.nativeStreaming`은 `channels.slack.streaming.nativeTransport`로 자동 마이그레이션됩니다. -## 입력 중 반응 fallback +## 입력 중 반응 대체 동작 -`typingReaction`은 OpenClaw가 답장을 처리하는 동안 인바운드 Slack 메시지에 임시 반응을 추가한 다음 실행이 끝나면 제거합니다. 이는 기본 "is typing..." 상태 표시기를 사용하는 스레드 답장 밖에서 가장 유용합니다. +`typingReaction`은 OpenClaw가 응답을 처리하는 동안 수신 Slack 메시지에 임시 반응을 추가한 뒤, 실행이 끝나면 제거합니다. 이는 기본 "입력 중..." 상태 표시기를 사용하는 스레드 응답 밖에서 가장 유용합니다. -확인 순서: +해결 순서: - `channels.slack.accounts..typingReaction` - `channels.slack.typingReaction` 참고: -- Slack은 숏코드(예: `"hourglass_flowing_sand"`)를 기대합니다. -- 반응은 최선형으로 처리되며, 답장 또는 실패 경로가 완료된 뒤 정리가 자동으로 시도됩니다. +- Slack은 쇼트코드(예: `"hourglass_flowing_sand"`)를 기대합니다. +- 반응은 최선형으로 처리되며, 응답 또는 실패 경로가 완료된 뒤 정리가 자동으로 시도됩니다. -## 미디어, 청킹 및 전달 +## 미디어, 청킹, 전달 - + Slack 파일 첨부는 Slack에서 호스팅되는 비공개 URL(토큰 인증 요청 흐름)에서 다운로드되며, 가져오기에 성공하고 크기 제한이 허용되면 미디어 저장소에 기록됩니다. 파일 플레이스홀더에는 Slack `fileId`가 포함되어 에이전트가 `download-file`로 원본 파일을 가져올 수 있습니다. - 다운로드에는 제한된 유휴 및 총 시간 제한이 사용됩니다. Slack 파일 검색이 멈추거나 실패하면 OpenClaw는 메시지 처리를 계속하고 파일 플레이스홀더로 대체합니다. + 다운로드에는 제한된 유휴 및 전체 시간 제한이 적용됩니다. Slack 파일 검색이 멈추거나 실패하면 OpenClaw는 메시지 처리를 계속하고 파일 플레이스홀더로 폴백합니다. - 런타임 인바운드 크기 상한은 `channels.slack.mediaMaxMb`로 재정의하지 않는 한 기본값이 `20MB`입니다. + 런타임 수신 크기 상한은 `channels.slack.mediaMaxMb`로 재정의하지 않는 한 기본값이 `20MB`입니다. - - - 텍스트 청크는 `channels.slack.textChunkLimit`를 사용합니다(기본값 4000). - - `channels.slack.chunkMode="newline"`은 단락 우선 분할을 활성화합니다. - - 파일 전송은 Slack 업로드 API를 사용하며 스레드 답장(`thread_ts`)을 포함할 수 있습니다. - - 아웃바운드 미디어 상한은 구성된 경우 `channels.slack.mediaMaxMb`를 따르며, 그렇지 않으면 채널 전송은 미디어 파이프라인의 MIME 종류 기본값을 사용합니다. + + - 텍스트 청크는 `channels.slack.textChunkLimit`(기본값 4000)을 사용합니다 + - `channels.slack.chunkMode="newline"`은 문단 우선 분할을 활성화합니다 + - 파일 전송은 Slack 업로드 API를 사용하며 스레드 응답(`thread_ts`)을 포함할 수 있습니다 + - 송신 미디어 상한은 구성된 경우 `channels.slack.mediaMaxMb`를 따릅니다. 그렇지 않으면 채널 전송은 미디어 파이프라인의 MIME 종류 기본값을 사용합니다 - - 선호되는 명시적 대상: + + 권장되는 명시적 대상: - DM에는 `user:` - 채널에는 `channel:` @@ -750,7 +769,7 @@ Slack 네이티브 텍스트 스트리밍 대신 초안 미리 보기를 사용 ## 명령 및 슬래시 동작 -슬래시 명령은 Slack에서 단일 구성 명령 또는 여러 네이티브 명령으로 나타납니다. 명령 기본값을 변경하려면 `channels.slack.slashCommand`를 구성하세요. +슬래시 명령은 Slack에서 단일 구성 명령 또는 여러 네이티브 명령으로 표시됩니다. 명령 기본값을 변경하려면 `channels.slack.slashCommand`를 구성하세요. - `enabled: false` - `name: "openclaw"` @@ -761,7 +780,7 @@ Slack 네이티브 텍스트 스트리밍 대신 초안 미리 보기를 사용 /openclaw /help ``` -네이티브 명령은 Slack 앱에서 [추가 매니페스트 설정](#additional-manifest-settings)이 필요하며, 대신 글로벌 구성에서 `channels.slack.commands.native: true` 또는 `commands.native: true`로 활성화됩니다. +네이티브 명령은 Slack 앱에 [추가 매니페스트 설정](#additional-manifest-settings)이 필요하며, 대신 전역 구성에서 `channels.slack.commands.native: true` 또는 `commands.native: true`로 활성화됩니다. - Slack에서는 네이티브 명령 자동 모드가 **꺼져** 있으므로 `commands.native: "auto"`는 Slack 네이티브 명령을 활성화하지 않습니다. @@ -772,19 +791,19 @@ Slack 네이티브 텍스트 스트리밍 대신 초안 미리 보기를 사용 네이티브 인수 메뉴는 선택된 옵션 값을 디스패치하기 전에 확인 모달을 표시하는 적응형 렌더링 전략을 사용합니다. - 최대 5개 옵션: 버튼 블록 -- 6~100개 옵션: 정적 선택 메뉴 -- 100개 초과 옵션: 상호작용 옵션 핸들러를 사용할 수 있을 때 비동기 옵션 필터링이 포함된 외부 선택 -- Slack 제한 초과: 인코딩된 옵션 값은 버튼으로 대체 +- 6-100개 옵션: 정적 선택 메뉴 +- 100개 초과 옵션: 상호작용 옵션 핸들러를 사용할 수 있을 때 비동기 옵션 필터링을 사용하는 외부 선택 +- Slack 제한 초과: 인코딩된 옵션 값은 버튼으로 폴백합니다 ```txt /think ``` -슬래시 세션은 `agent::slack:slash:` 같은 격리된 키를 사용하며, 여전히 `CommandTargetSessionKey`를 사용해 명령 실행을 대상 대화 세션으로 라우팅합니다. +슬래시 세션은 `agent::slack:slash:` 같은 격리된 키를 사용하며, 여전히 `CommandTargetSessionKey`를 사용해 대상 대화 세션으로 명령 실행을 라우팅합니다. -## 대화형 답장 +## 상호작용 응답 -Slack은 에이전트가 작성한 대화형 답장 컨트롤을 렌더링할 수 있지만, 이 기능은 기본적으로 비활성화되어 있습니다. +Slack은 에이전트가 작성한 상호작용 응답 컨트롤을 렌더링할 수 있지만, 이 기능은 기본적으로 비활성화되어 있습니다. 전역으로 활성화: @@ -818,44 +837,43 @@ Slack은 에이전트가 작성한 대화형 답장 컨트롤을 렌더링할 } ``` -활성화되면 에이전트는 Slack 전용 답장 지시문을 내보낼 수 있습니다. +활성화하면 에이전트가 Slack 전용 응답 지시문을 내보낼 수 있습니다. - `[[slack_buttons: Approve:approve, Reject:reject]]` - `[[slack_select: Choose a target | Canary:canary, Production:production]]` -이 지시문은 Slack Block Kit으로 컴파일되며 클릭 또는 선택을 기존 Slack 상호작용 이벤트 경로를 통해 다시 라우팅합니다. +이 지시문은 Slack Block Kit으로 컴파일되며, 클릭 또는 선택을 기존 Slack 상호작용 이벤트 경로를 통해 다시 라우팅합니다. 참고: -- 이것은 Slack 전용 UI입니다. 다른 채널은 Slack Block Kit 지시문을 자체 버튼 시스템으로 변환하지 않습니다. -- 대화형 콜백 값은 원시 에이전트 작성 값이 아니라 OpenClaw가 생성한 불투명 토큰입니다. -- 생성된 대화형 블록이 Slack Block Kit 제한을 초과할 경우, OpenClaw는 유효하지 않은 블록 페이로드를 보내는 대신 원래 텍스트 답장으로 대체합니다. +- 이는 Slack 전용 UI입니다. 다른 채널은 Slack Block Kit 지시문을 자체 버튼 시스템으로 변환하지 않습니다. +- 상호작용 콜백 값은 에이전트가 작성한 원시 값이 아니라 OpenClaw가 생성한 불투명 토큰입니다. +- 생성된 상호작용 블록이 Slack Block Kit 제한을 초과하면 OpenClaw는 잘못된 블록 페이로드를 보내는 대신 원래 텍스트 응답으로 폴백합니다. ## Slack의 Exec 승인 -Slack은 Web UI 또는 터미널로 대체하는 대신, 대화형 버튼과 상호작용을 갖춘 네이티브 승인 클라이언트로 동작할 수 있습니다. +Slack은 Web UI 또는 터미널로 폴백하는 대신, 상호작용 버튼과 상호작용을 갖춘 네이티브 승인 클라이언트로 동작할 수 있습니다. - Exec 승인은 네이티브 DM/채널 라우팅에 `channels.slack.execApprovals.*`를 사용합니다. -- 요청이 이미 Slack에 도착했고 승인 ID 종류가 `plugin:`인 경우, Plugin 승인은 동일한 Slack 네이티브 버튼 표면을 통해 계속 해결될 수 있습니다. +- 요청이 이미 Slack에 도착했고 승인 ID 종류가 `plugin:`인 경우, Plugin 승인도 같은 Slack 네이티브 버튼 표면을 통해 해결될 수 있습니다. - 승인자 권한 부여는 계속 적용됩니다. 승인자로 식별된 사용자만 Slack을 통해 요청을 승인하거나 거부할 수 있습니다. 이는 다른 채널과 동일한 공유 승인 버튼 표면을 사용합니다. Slack 앱 설정에서 `interactivity`가 활성화되면 승인 프롬프트가 대화 안에 Block Kit 버튼으로 직접 렌더링됩니다. 해당 버튼이 있으면 그것이 기본 승인 UX입니다. OpenClaw는 도구 결과가 채팅 -승인을 사용할 수 없다고 하거나 수동 승인이 유일한 경로라고 말할 때에만 -수동 `/approve` 명령을 포함해야 합니다. +승인을 사용할 수 없다고 하거나 수동 승인이 유일한 경로라고 할 때만 수동 +`/approve` 명령을 포함해야 합니다. 구성 경로: - `channels.slack.execApprovals.enabled` -- `channels.slack.execApprovals.approvers`(선택 사항, 가능하면 `commands.ownerAllowFrom`으로 대체) +- `channels.slack.execApprovals.approvers`(선택 사항. 가능하면 `commands.ownerAllowFrom`으로 폴백) - `channels.slack.execApprovals.target`(`dm` | `channel` | `both`, 기본값: `dm`) - `agentFilter`, `sessionFilter` -Slack은 `enabled`가 설정되지 않았거나 `"auto"`이고 하나 이상의 -승인자가 확인되면 네이티브 Exec 승인을 자동으로 활성화합니다. Slack을 네이티브 승인 클라이언트로 명시적으로 비활성화하려면 `enabled: false`를 설정하세요. +Slack은 `enabled`가 설정되지 않았거나 `"auto"`이고 하나 이상의 승인자가 확인되면 네이티브 exec 승인을 자동으로 활성화합니다. Slack을 네이티브 승인 클라이언트로 명시적으로 비활성화하려면 `enabled: false`를 설정하세요. 승인자가 확인될 때 네이티브 승인을 강제로 켜려면 `enabled: true`를 설정하세요. -명시적 Slack Exec 승인 구성이 없을 때의 기본 동작: +명시적 Slack exec 승인 구성이 없을 때의 기본 동작: ```json5 { @@ -882,34 +900,32 @@ Slack은 `enabled`가 설정되지 않았거나 `"auto"`이고 하나 이상의 } ``` -공유 `approvals.exec` 전달은 별도입니다. Exec 승인 프롬프트를 다른 채팅 또는 명시적인 대역 외 대상에도 -라우팅해야 할 때만 사용하세요. 공유 `approvals.plugin` 전달도 -별도입니다. 해당 요청이 이미 Slack에 도착한 경우 Slack 네이티브 버튼으로 Plugin 승인을 계속 해결할 수 있습니다. +공유 `approvals.exec` 포워딩은 별개입니다. exec 승인 프롬프트가 다른 채팅이나 명시적 대역 외 대상으로도 라우팅되어야 할 때만 사용하세요. 공유 `approvals.plugin` 포워딩도 별개입니다. 해당 요청이 이미 Slack에 도착한 경우 Slack 네이티브 버튼은 여전히 Plugin 승인을 해결할 수 있습니다. -동일 채팅 `/approve`는 이미 명령을 지원하는 Slack 채널과 DM에서도 동작합니다. 전체 승인 전달 모델은 [Exec 승인](/ko/tools/exec-approvals)을 참고하세요. +동일 채팅 `/approve`도 이미 명령을 지원하는 Slack 채널과 DM에서 작동합니다. 전체 승인 포워딩 모델은 [Exec 승인](/ko/tools/exec-approvals)을 참조하세요. ## 이벤트 및 운영 동작 -- 메시지 편집/삭제는 시스템 이벤트로 매핑됩니다. -- 스레드 브로드캐스트("채널에도 보내기" 스레드 답장)는 일반 사용자 메시지로 처리됩니다. +- 메시지 수정/삭제는 시스템 이벤트로 매핑됩니다. +- 스레드 브로드캐스트("채널에도 보내기" 스레드 응답)는 일반 사용자 메시지로 처리됩니다. - 반응 추가/제거 이벤트는 시스템 이벤트로 매핑됩니다. -- 멤버 참여/퇴장, 채널 생성/이름 변경, 핀 추가/제거 이벤트는 시스템 이벤트로 매핑됩니다. -- `configWrites`가 활성화되면 `channel_id_changed`가 채널 구성 키를 마이그레이션할 수 있습니다. -- 채널 주제/목적 메타데이터는 신뢰할 수 없는 컨텍스트로 취급되며 라우팅 컨텍스트에 삽입될 수 있습니다. -- 스레드 시작 메시지와 초기 스레드 기록 컨텍스트 시딩은 해당되는 경우 구성된 발신자 허용 목록으로 필터링됩니다. -- 블록 작업과 모달 상호작용은 풍부한 페이로드 필드를 포함한 구조화된 `Slack interaction: ...` 시스템 이벤트를 내보냅니다. - - 블록 작업: 선택된 값, 레이블, 피커 값 및 `workflow_*` 메타데이터 - - 라우팅된 채널 메타데이터와 양식 입력을 포함한 모달 `view_submission` 및 `view_closed` 이벤트 +- 멤버 참여/나가기, 채널 생성/이름 변경, 핀 추가/제거 이벤트는 시스템 이벤트로 매핑됩니다. +- `configWrites`가 활성화된 경우 `channel_id_changed`는 채널 구성 키를 마이그레이션할 수 있습니다. +- 채널 주제/목적 메타데이터는 신뢰할 수 없는 컨텍스트로 취급되며 라우팅 컨텍스트에 주입될 수 있습니다. +- 스레드 시작자와 초기 스레드 기록 컨텍스트 시딩은 해당되는 경우 구성된 발신자 허용 목록으로 필터링됩니다. +- 블록 작업과 모달 상호작용은 풍부한 페이로드 필드를 포함하는 구조화된 `Slack interaction: ...` 시스템 이벤트를 내보냅니다. + - 블록 작업: 선택된 값, 레이블, 피커 값, `workflow_*` 메타데이터 + - 라우팅된 채널 메타데이터와 폼 입력을 포함한 모달 `view_submission` 및 `view_closed` 이벤트 ## 구성 참조 기본 참조: [구성 참조 - Slack](/ko/gateway/config-channels#slack). - + - 모드/인증: `mode`, `botToken`, `appToken`, `signingSecret`, `webhookPath`, `accounts.*` - DM 접근: `dm.enabled`, `dmPolicy`, `allowFrom`(레거시: `dm.policy`, `dm.allowFrom`), `dm.groupEnabled`, `dm.groupChannels` -- 호환성 토글: `dangerouslyAllowNameMatching`(비상용, 필요하지 않으면 꺼둠) +- 호환성 토글: `dangerouslyAllowNameMatching`(비상용. 필요하지 않으면 꺼 두세요) - 채널 접근: `groupPolicy`, `channels.*`, `channels.*.users`, `channels.*.requireMention` - 스레딩/기록: `replyToMode`, `replyToModeByChatType`, `thread.*`, `historyLimit`, `dmHistoryLimit`, `dms.*.historyLimit` - 전달: `textChunkLimit`, `chunkMode`, `mediaMaxMb`, `streaming`, `streaming.nativeTransport`, `streaming.preview.toolProgress` @@ -920,11 +936,11 @@ Slack은 `enabled`가 설정되지 않았거나 `"auto"`이고 하나 이상의 ## 문제 해결 - + 순서대로 확인하세요. - `groupPolicy` - - 채널 허용 목록(`channels.slack.channels`) — **키는 채널 이름(`#channel-name`)이 아니라 채널 ID**(`C12345678`)여야 합니다. 채널 라우팅은 기본적으로 ID 우선이므로, `groupPolicy: "allowlist"`에서 이름 기반 키는 조용히 실패합니다. ID를 찾으려면 Slack에서 채널을 우클릭 → **링크 복사** — URL 끝의 `C...` 값이 채널 ID입니다. + - 채널 허용 목록(`channels.slack.channels`) — **키는 채널 이름**(`#channel-name`)이 아니라 **채널 ID**(`C12345678`)여야 합니다. 채널 라우팅은 기본적으로 ID 우선이므로 `groupPolicy: "allowlist"`에서 이름 기반 키는 조용히 실패합니다. ID를 찾으려면 Slack에서 채널을 마우스 오른쪽 버튼으로 클릭 → **링크 복사** — URL 끝의 `C...` 값이 채널 ID입니다. - `requireMention` - 채널별 `users` 허용 목록 @@ -938,7 +954,7 @@ openclaw doctor - + 확인하세요. - `channels.slack.dm.enabled` @@ -946,7 +962,7 @@ openclaw doctor - 페어링 승인 / 허용 목록 항목 - Slack Assistant DM 이벤트: `drop message_changed`를 언급하는 상세 로그는 일반적으로 Slack이 메시지 메타데이터에 복구 가능한 사람 발신자 없이 - 편집된 Assistant 스레드 이벤트를 보냈다는 뜻입니다. + 수정된 Assistant 스레드 이벤트를 보냈다는 뜻입니다 ```bash openclaw pairing list slack @@ -954,33 +970,33 @@ openclaw pairing list slack - - Slack 앱 설정에서 봇 + 앱 토큰과 Socket Mode 활성화를 검증하세요. + + Slack 앱 설정에서 bot + app 토큰과 Socket Mode 활성화를 검증하세요. `openclaw channels status --probe --json`에 `botTokenStatus` 또는 - `appTokenStatus: "configured_unavailable"`가 표시되면, Slack 계정은 - 구성되었지만 현재 런타임이 SecretRef 기반 값을 확인할 수 없었다는 뜻입니다. + `appTokenStatus: "configured_unavailable"`가 표시되면 Slack 계정은 + 구성되어 있지만 현재 런타임이 SecretRef 기반 값을 확인할 수 없었다는 뜻입니다. - + 검증하세요. - - 서명 시크릿 + - 서명 비밀 - Webhook 경로 - Slack 요청 URL(이벤트 + 상호작용 + 슬래시 명령) - HTTP 계정별 고유한 `webhookPath` 계정 스냅샷에 `signingSecretStatus: "configured_unavailable"`가 표시되면, - HTTP 계정은 구성되었지만 현재 런타임이 SecretRef 기반 서명 시크릿을 + HTTP 계정은 구성되어 있지만 현재 런타임이 SecretRef 기반 서명 비밀을 확인할 수 없었다는 뜻입니다. - + 의도한 것이 무엇인지 확인하세요. - - Slack에 등록된 일치하는 슬래시 명령과 함께 사용하는 네이티브 명령 모드(`channels.slack.commands.native: true`) + - Slack에 등록된 일치하는 슬래시 명령이 있는 네이티브 명령 모드(`channels.slack.commands.native: true`) - 또는 단일 슬래시 명령 모드(`channels.slack.slashCommand.enabled: true`) `commands.useAccessGroups`와 채널/사용자 허용 목록도 확인하세요. @@ -988,19 +1004,19 @@ openclaw pairing list slack -## 첨부 비전 참조 +## 첨부 파일 비전 참조 -Slack 파일 다운로드에 성공하고 크기 제한이 허용되면 Slack은 다운로드된 미디어를 에이전트 턴에 첨부할 수 있습니다. 이미지 파일은 미디어 이해 경로를 통과하거나 비전 지원 답장 모델에 직접 전달될 수 있습니다. 다른 파일은 이미지 입력으로 처리되지 않고 다운로드 가능한 파일 컨텍스트로 보존됩니다. +Slack 파일 다운로드에 성공하고 크기 제한이 허용되면 Slack은 다운로드된 미디어를 에이전트 턴에 첨부할 수 있습니다. 이미지 파일은 미디어 이해 경로를 통해 전달되거나 비전 가능 응답 모델에 직접 전달될 수 있습니다. 다른 파일은 이미지 입력으로 취급되지 않고 다운로드 가능한 파일 컨텍스트로 유지됩니다. ### 지원되는 미디어 유형 -| 미디어 유형 | 소스 | 현재 동작 | 참고 | +| 미디어 유형 | 소스 | 현재 동작 | 참고 사항 | | ------------------------------ | -------------------- | --------------------------------------------------------------------------------- | ------------------------------------------------------------------------- | -| JPEG / PNG / GIF / WebP 이미지 | Slack 파일 URL | 다운로드되어 비전 지원 처리를 위해 턴에 첨부됨 | 파일당 제한: `channels.slack.mediaMaxMb`(기본값 20 MB) | -| PDF 파일 | Slack 파일 URL | 다운로드되어 `download-file` 또는 `pdf` 같은 도구의 파일 컨텍스트로 노출됨 | Slack 인바운드는 PDF를 이미지 비전 입력으로 자동 변환하지 않음 | -| 기타 파일 | Slack 파일 URL | 가능한 경우 다운로드되어 파일 컨텍스트로 노출됨 | 바이너리 파일은 이미지 입력으로 취급되지 않음 | -| 스레드 답글 | 스레드 시작 메시지 파일 | 답글에 직접 미디어가 없으면 루트 메시지 파일을 컨텍스트로 하이드레이션할 수 있음 | 파일만 있는 시작 메시지는 첨부 파일 플레이스홀더를 사용함 | -| 다중 이미지 메시지 | 여러 Slack 파일 | 각 파일이 독립적으로 평가됨 | Slack 처리는 메시지당 최대 8개 파일로 제한됨 | +| JPEG / PNG / GIF / WebP 이미지 | Slack 파일 URL | 다운로드되어 비전 지원 처리를 위해 턴에 첨부됨 | 파일별 한도: `channels.slack.mediaMaxMb`(기본값 20 MB) | +| PDF 파일 | Slack 파일 URL | 다운로드되어 `download-file` 또는 `pdf` 같은 도구의 파일 컨텍스트로 노출됨 | Slack 인바운드는 PDF를 이미지 비전 입력으로 자동 변환하지 않음 | +| 기타 파일 | Slack 파일 URL | 가능한 경우 다운로드되어 파일 컨텍스트로 노출됨 | 바이너리 파일은 이미지 입력으로 취급되지 않음 | +| 스레드 답글 | 스레드 시작자 파일 | 답글에 직접 미디어가 없을 때 루트 메시지 파일을 컨텍스트로 하이드레이션할 수 있음 | 파일만 있는 시작자는 첨부 파일 플레이스홀더를 사용함 | +| 다중 이미지 메시지 | 여러 Slack 파일 | 각 파일은 독립적으로 평가됨 | Slack 처리는 메시지당 8개 파일로 제한됨 | ### 인바운드 파이프라인 @@ -1010,15 +1026,15 @@ Slack 파일 다운로드에 성공하고 크기 제한이 허용되면 Slack은 2. 성공하면 파일이 미디어 저장소에 기록됩니다. 3. 다운로드된 미디어 경로와 콘텐츠 유형이 인바운드 컨텍스트에 추가됩니다. 4. 이미지 지원 모델/도구 경로는 해당 컨텍스트의 이미지 첨부를 사용할 수 있습니다. -5. 이미지가 아닌 파일은 이를 처리할 수 있는 도구에서 파일 메타데이터 또는 미디어 참조로 계속 사용할 수 있습니다. +5. 이미지가 아닌 파일은 이를 처리할 수 있는 도구를 위해 파일 메타데이터 또는 미디어 참조로 계속 사용할 수 있습니다. ### 스레드 루트 첨부 상속 메시지가 스레드에 도착하면(`thread_ts` 부모가 있음): -- 답글 자체에 직접 미디어가 없고 포함된 루트 메시지에 파일이 있으면, Slack은 루트 파일을 스레드 시작 컨텍스트로 하이드레이션할 수 있습니다. +- 답글 자체에 직접 미디어가 없고 포함된 루트 메시지에 파일이 있으면 Slack은 루트 파일을 스레드 시작자 컨텍스트로 하이드레이션할 수 있습니다. - 직접 답글 첨부가 루트 메시지 첨부보다 우선합니다. -- 파일만 있고 텍스트가 없는 루트 메시지는 첨부 파일 플레이스홀더로 표현되어 fallback이 해당 파일을 계속 포함할 수 있습니다. +- 파일만 있고 텍스트가 없는 루트 메시지는 대체 동작이 여전히 파일을 포함할 수 있도록 첨부 파일 플레이스홀더로 표현됩니다. ### 다중 첨부 처리 @@ -1031,29 +1047,29 @@ Slack 파일 다운로드에 성공하고 크기 제한이 허용되면 Slack은 ### 크기, 다운로드, 모델 제한 -- **크기 제한**: 기본값은 파일당 20 MB입니다. `channels.slack.mediaMaxMb`로 구성할 수 있습니다. +- **크기 한도**: 파일당 기본 20 MB입니다. `channels.slack.mediaMaxMb`로 구성할 수 있습니다. - **다운로드 실패**: Slack이 제공할 수 없는 파일, 만료된 URL, 접근할 수 없는 파일, 크기 초과 파일, Slack 인증/로그인 HTML 응답은 지원되지 않는 형식으로 보고되지 않고 건너뜁니다. -- **비전 모델**: 이미지 분석은 비전을 지원하는 경우 활성 답글 모델을 사용하거나, `agents.defaults.imageModel`에 구성된 이미지 모델을 사용합니다. +- **비전 모델**: 이미지 분석은 비전을 지원하는 경우 활성 답글 모델을 사용하거나 `agents.defaults.imageModel`에 구성된 이미지 모델을 사용합니다. ### 알려진 제한 -| 시나리오 | 현재 동작 | 해결 방법 | -| -------------------------------------- | ---------------------------------------------------------------------------- | -------------------------------------------------------------------------- | -| 만료된 Slack 파일 URL | 파일을 건너뜀; 오류가 표시되지 않음 | Slack에 파일을 다시 업로드 | -| 비전 모델이 구성되지 않음 | 이미지 첨부가 미디어 참조로 저장되지만 이미지로 분석되지 않음 | `agents.defaults.imageModel`을 구성하거나 비전 지원 답글 모델 사용 | -| 매우 큰 이미지(기본값 기준 > 20 MB) | 크기 제한에 따라 건너뜀 | Slack이 허용하는 경우 `channels.slack.mediaMaxMb` 증가 | -| 전달/공유된 첨부 | 텍스트 및 Slack 호스팅 이미지/파일 미디어는 최선 노력으로 처리됨 | OpenClaw 스레드에서 직접 다시 공유 | -| PDF 첨부 | 파일/미디어 컨텍스트로 저장되며 이미지 비전을 통해 자동 라우팅되지 않음 | 파일 메타데이터에는 `download-file`을 사용하거나 PDF 분석에는 `pdf` 도구 사용 | +| 시나리오 | 현재 동작 | 해결 방법 | +| ------------------------------------- | ------------------------------------------------------------------------------ | -------------------------------------------------------------------------- | +| 만료된 Slack 파일 URL | 파일을 건너뜀; 오류가 표시되지 않음 | Slack에 파일을 다시 업로드 | +| 비전 모델이 구성되지 않음 | 이미지 첨부가 미디어 참조로 저장되지만 이미지로 분석되지 않음 | `agents.defaults.imageModel`을 구성하거나 비전 지원 답글 모델 사용 | +| 매우 큰 이미지(기본값 기준 > 20 MB) | 크기 한도에 따라 건너뜀 | Slack이 허용하는 경우 `channels.slack.mediaMaxMb` 증가 | +| 전달/공유된 첨부 | 텍스트와 Slack 호스팅 이미지/파일 미디어는 최선형으로 처리됨 | OpenClaw 스레드에서 직접 다시 공유 | +| PDF 첨부 | 파일/미디어 컨텍스트로 저장되며 이미지 비전을 통해 자동 라우팅되지 않음 | 파일 메타데이터에는 `download-file`을 사용하거나 PDF 분석에는 `pdf` 도구 사용 | ### 관련 문서 - [미디어 이해 파이프라인](/ko/nodes/media-understanding) - [PDF 도구](/ko/tools/pdf) -- 에픽: [#51349](https://github.com/openclaw/openclaw/issues/51349) — Slack 첨부 비전 활성화 +- Epic: [#51349](https://github.com/openclaw/openclaw/issues/51349) — Slack 첨부 비전 활성화 - 회귀 테스트: [#51353](https://github.com/openclaw/openclaw/issues/51353) - 라이브 검증: [#51354](https://github.com/openclaw/openclaw/issues/51354) -## 관련 항목 +## 관련 diff --git a/docs/ko/channels/telegram.md b/docs/ko/channels/telegram.md index cc62cce8d..b238a80a7 100644 --- a/docs/ko/channels/telegram.md +++ b/docs/ko/channels/telegram.md @@ -1,38 +1,38 @@ --- read_when: - Telegram 기능 또는 Webhook 작업하기 -summary: Telegram 봇 지원 상태, 기능 및 설정 +summary: Telegram 봇 지원 상태, 기능 및 구성 title: Telegram x-i18n: - generated_at: "2026-05-04T06:22:43Z" + generated_at: "2026-05-04T07:02:47Z" model: gpt-5.5 provider: openai - source_hash: c7f49db5f3fe8fd724e53a2ae3d226446f248bf9d021fcc01c1cf816649381d2 + source_hash: 6ef1b019a6a0e261b33972b5edffaedd29310b1333d112bade2e79e9d56887c6 source_path: channels/telegram.md workflow: 16 --- -프로덕션에서 사용할 준비가 된 grammY 기반 봇 DM 및 그룹 지원입니다. 롱 폴링이 기본 모드이며, Webhook 모드는 선택 사항입니다. +grammY를 통해 bot DM 및 그룹에서 프로덕션 준비 완료 상태로 사용할 수 있습니다. Long polling이 기본 모드이며, webhook 모드는 선택 사항입니다. Telegram의 기본 DM 정책은 페어링입니다. - 채널 간 진단 및 복구 플레이북입니다. + 교차 채널 진단 및 복구 플레이북입니다. - 전체 채널 구성 패턴과 예시입니다. + 전체 채널 구성 패턴 및 예시입니다. ## 빠른 설정 - + Telegram을 열고 **@BotFather**와 채팅합니다(핸들이 정확히 `@BotFather`인지 확인). - `/newbot`을 실행하고 안내를 따른 뒤 토큰을 저장합니다. + `/newbot`을 실행하고 프롬프트를 따른 뒤 토큰을 저장합니다. @@ -51,12 +51,12 @@ x-i18n: } ``` - 환경 변수 폴백: `TELEGRAM_BOT_TOKEN=...`(기본 계정만 해당). - Telegram은 `openclaw channels login telegram`을 사용하지 **않습니다**. config/env에 토큰을 구성한 다음 Gateway를 시작하세요. + Env 폴백: `TELEGRAM_BOT_TOKEN=...`(기본 계정 전용). + Telegram은 `openclaw channels login telegram`을 사용하지 **않습니다**. config/env에서 토큰을 구성한 다음 Gateway를 시작하세요. - + ```bash openclaw gateway @@ -68,77 +68,77 @@ openclaw pairing approve telegram - - 그룹에 봇을 추가한 다음, 액세스 모델에 맞게 `channels.telegram.groups`와 `groupPolicy`를 설정합니다. + + bot을 그룹에 추가한 다음, 접근 모델에 맞게 `channels.telegram.groups` 및 `groupPolicy`를 설정합니다. -토큰 확인 순서는 계정을 인식합니다. 실제로는 구성 값이 환경 변수 폴백보다 우선하며, `TELEGRAM_BOT_TOKEN`은 기본 계정에만 적용됩니다. +토큰 확인 순서는 계정을 인식합니다. 실제로는 config 값이 env 폴백보다 우선하며, `TELEGRAM_BOT_TOKEN`은 기본 계정에만 적용됩니다. -## Telegram 쪽 설정 +## Telegram 측 설정 - - Telegram 봇은 기본적으로 **개인정보 보호 모드**를 사용하며, 이 모드는 봇이 받을 수 있는 그룹 메시지를 제한합니다. + + Telegram bot은 기본적으로 **프라이버시 모드**를 사용하며, 이 모드는 bot이 수신하는 그룹 메시지를 제한합니다. - 봇이 모든 그룹 메시지를 확인해야 한다면 다음 중 하나를 수행하세요. + bot이 모든 그룹 메시지를 확인해야 한다면 다음 중 하나를 수행합니다. - - `/setprivacy`로 개인정보 보호 모드를 비활성화하거나 - - 봇을 그룹 관리자로 지정합니다. + - `/setprivacy`를 통해 프라이버시 모드를 비활성화하거나 + - bot을 그룹 관리자로 지정합니다. - 개인정보 보호 모드를 전환할 때는 Telegram이 변경 사항을 적용하도록 각 그룹에서 봇을 제거한 뒤 다시 추가하세요. + 프라이버시 모드를 전환할 때는 Telegram이 변경 사항을 적용하도록 각 그룹에서 bot을 제거한 뒤 다시 추가하세요. 관리자 상태는 Telegram 그룹 설정에서 제어됩니다. - 관리자 봇은 모든 그룹 메시지를 받으므로, 상시 동작하는 그룹 동작에 유용합니다. + 관리자 bot은 모든 그룹 메시지를 수신하므로, 항상 켜져 있는 그룹 동작에 유용합니다. - - 그룹 추가를 허용/거부하는 `/setjoingroups` - - 그룹 가시성 동작을 위한 `/setprivacy` + - 그룹 추가 허용/거부용 `/setjoingroups` + - 그룹 표시 동작용 `/setprivacy` -## 액세스 제어 및 활성화 +## 접근 제어 및 활성화 - `channels.telegram.dmPolicy`는 직접 메시지 액세스를 제어합니다. + `channels.telegram.dmPolicy`는 다이렉트 메시지 접근을 제어합니다. - `pairing`(기본값) - `allowlist`(`allowFrom`에 하나 이상의 발신자 ID 필요) - `open`(`allowFrom`에 `"*"` 포함 필요) - `disabled` - `allowFrom: ["*"]`와 함께 `dmPolicy: "open"`을 사용하면 봇 사용자 이름을 찾거나 추측한 모든 Telegram 계정이 봇에 명령할 수 있습니다. 도구가 엄격히 제한된 의도적인 공개 봇에만 사용하세요. 단일 소유자 봇은 숫자 사용자 ID와 함께 `allowlist`를 사용해야 합니다. + `allowFrom: ["*"]`와 함께 `dmPolicy: "open"`을 사용하면 bot 사용자 이름을 찾거나 추측한 모든 Telegram 계정이 bot에 명령을 보낼 수 있습니다. 도구가 엄격히 제한된 의도적인 공개 bot에만 사용하세요. 단일 소유자 bot은 숫자 사용자 ID와 함께 `allowlist`를 사용해야 합니다. `channels.telegram.allowFrom`은 숫자 Telegram 사용자 ID를 허용합니다. `telegram:` / `tg:` 접두사는 허용되며 정규화됩니다. - 다중 계정 구성에서는 제한적인 최상위 `channels.telegram.allowFrom`이 안전 경계로 처리됩니다. 병합 후 유효 계정 허용 목록에 명시적 와일드카드가 여전히 포함되어 있지 않으면, 계정 수준의 `allowFrom: ["*"]` 항목이 해당 계정을 공개로 만들지 않습니다. - 빈 `allowFrom`과 함께 `dmPolicy: "allowlist"`를 사용하면 모든 DM이 차단되며 구성 검증에서 거부됩니다. - 설정은 숫자 사용자 ID만 요청합니다. - 업그레이드 후 구성에 `@username` 허용 목록 항목이 포함되어 있다면 `openclaw doctor --fix`를 실행해 이를 확인하세요(최선의 시도이며, Telegram 봇 토큰 필요). - 이전에 페어링 저장소 허용 목록 파일에 의존했다면, `openclaw doctor --fix`가 allowlist 흐름에서 항목을 `channels.telegram.allowFrom`으로 복구할 수 있습니다(예: `dmPolicy: "allowlist"`에 아직 명시적 ID가 없는 경우). + 다중 계정 구성에서는 제한적인 최상위 `channels.telegram.allowFrom`이 안전 경계로 처리됩니다. 계정 수준의 `allowFrom: ["*"]` 항목은 병합 후 유효 계정 allowlist에 명시적 와일드카드가 여전히 포함되어 있지 않으면 해당 계정을 공개로 만들지 않습니다. + 비어 있는 `allowFrom`과 함께 `dmPolicy: "allowlist"`를 사용하면 모든 DM이 차단되며 config 유효성 검사에서 거부됩니다. + 설정에서는 숫자 사용자 ID만 요청합니다. + 업그레이드 후 config에 `@username` allowlist 항목이 포함되어 있다면, `openclaw doctor --fix`를 실행하여 이를 해석하세요(최선의 노력 방식이며 Telegram bot 토큰이 필요함). + 이전에 pairing-store allowlist 파일에 의존했다면, `openclaw doctor --fix`가 allowlist 흐름에서 항목을 `channels.telegram.allowFrom`으로 복구할 수 있습니다(예: `dmPolicy: "allowlist"`에 아직 명시적 ID가 없는 경우). - 단일 소유자 봇의 경우, 이전 페어링 승인에 의존하는 대신 액세스 정책이 구성에 지속되도록 명시적인 숫자 `allowFrom` ID와 함께 `dmPolicy: "allowlist"`를 사용하는 것이 좋습니다. + 단일 소유자 bot의 경우, 이전 페어링 승인에 의존하는 대신 접근 정책을 config에 지속적으로 유지하려면 명시적 숫자 `allowFrom` ID와 함께 `dmPolicy: "allowlist"`를 사용하는 것이 좋습니다. - 흔한 혼동: DM 페어링 승인은 "이 발신자가 모든 곳에서 승인되었다"는 뜻이 아닙니다. - 페어링은 DM 액세스를 부여합니다. 명령 소유자가 아직 없으면, 첫 승인된 페어링은 소유자 전용 명령과 실행 승인이 명시적인 운영자 계정을 갖도록 `commands.ownerAllowFrom`도 설정합니다. - 그룹 발신자 인증은 여전히 명시적인 구성 허용 목록에서 가져옵니다. - "한 번 승인되면 DM과 그룹 명령이 모두 작동"하기를 원한다면 숫자 Telegram 사용자 ID를 `channels.telegram.allowFrom`에 넣으세요. 소유자 전용 명령의 경우 `commands.ownerAllowFrom`에 `telegram:`가 포함되어 있는지 확인하세요. + 흔한 혼동: DM 페어링 승인이 "이 발신자는 모든 곳에서 승인됨"을 의미하지는 않습니다. + 페어링은 DM 접근을 부여합니다. 명령 소유자가 아직 없으면, 첫 번째 승인된 페어링은 소유자 전용 명령 및 exec 승인이 명시적 운영자 계정을 갖도록 `commands.ownerAllowFrom`도 설정합니다. + 그룹 발신자 승인은 여전히 명시적 config allowlist에서 가져옵니다. + "한 번 승인되면 DM과 그룹 명령이 모두 작동"하도록 하려면 숫자 Telegram 사용자 ID를 `channels.telegram.allowFrom`에 넣으세요. 소유자 전용 명령의 경우 `commands.ownerAllowFrom`에 `telegram:`가 포함되어 있는지 확인하세요. ### Telegram 사용자 ID 찾기 - 더 안전한 방법(서드파티 봇 없음): + 더 안전한 방법(타사 bot 없음): - 1. 봇에 DM을 보냅니다. + 1. bot에 DM을 보냅니다. 2. `openclaw logs --follow`를 실행합니다. 3. `from.id`를 확인합니다. @@ -148,35 +148,35 @@ openclaw pairing approve telegram curl "https://api.telegram.org/bot/getUpdates" ``` - 서드파티 방법(개인정보 보호 수준 낮음): `@userinfobot` 또는 `@getidsbot`. + 타사 방법(프라이버시가 더 낮음): `@userinfobot` 또는 `@getidsbot`. - - 두 가지 제어가 함께 적용됩니다. + + 두 제어 항목이 함께 적용됩니다. 1. **허용되는 그룹**(`channels.telegram.groups`) - - `groups` 구성이 없음: + - `groups` config 없음: - `groupPolicy: "open"`인 경우: 모든 그룹이 그룹 ID 검사를 통과할 수 있음 - `groupPolicy: "allowlist"`(기본값)인 경우: `groups` 항목(또는 `"*"`)을 추가할 때까지 그룹이 차단됨 - - `groups`가 구성됨: 허용 목록처럼 동작(명시적 ID 또는 `"*"`) + - `groups`가 구성됨: allowlist로 동작(명시적 ID 또는 `"*"`) 2. **그룹에서 허용되는 발신자**(`channels.telegram.groupPolicy`) - `open` - `allowlist`(기본값) - `disabled` - `groupAllowFrom`은 그룹 발신자 필터링에 사용됩니다. 설정하지 않으면 Telegram은 `allowFrom`으로 폴백합니다. + `groupAllowFrom`은 그룹 발신자 필터링에 사용됩니다. 설정되지 않은 경우 Telegram은 `allowFrom`으로 폴백합니다. `groupAllowFrom` 항목은 숫자 Telegram 사용자 ID여야 합니다(`telegram:` / `tg:` 접두사는 정규화됨). - Telegram 그룹 또는 슈퍼그룹 채팅 ID를 `groupAllowFrom`에 넣지 마세요. 음수 채팅 ID는 `channels.telegram.groups` 아래에 둡니다. - 숫자가 아닌 항목은 발신자 인증에서 무시됩니다. - 보안 경계(`2026.2.25+`): 그룹 발신자 인증은 DM 페어링 저장소 승인을 상속하지 **않습니다**. - 페어링은 DM 전용으로 유지됩니다. 그룹의 경우 `groupAllowFrom` 또는 그룹별/주제별 `allowFrom`을 설정하세요. - `groupAllowFrom`이 설정되지 않은 경우 Telegram은 페어링 저장소가 아니라 구성의 `allowFrom`으로 폴백합니다. - 단일 소유자 봇의 실용적인 패턴: 사용자 ID를 `channels.telegram.allowFrom`에 설정하고, `groupAllowFrom`은 설정하지 않은 채 대상 그룹을 `channels.telegram.groups` 아래에서 허용합니다. - 런타임 참고: `channels.telegram`이 완전히 누락된 경우, `channels.defaults.groupPolicy`가 명시적으로 설정되어 있지 않으면 런타임은 fail-closed `groupPolicy="allowlist"`를 기본값으로 사용합니다. + Telegram 그룹 또는 슈퍼그룹 채팅 ID를 `groupAllowFrom`에 넣지 마세요. 음수 채팅 ID는 `channels.telegram.groups` 아래에 있어야 합니다. + 숫자가 아닌 항목은 발신자 승인에서 무시됩니다. + 보안 경계(`2026.2.25+`): 그룹 발신자 인증은 DM pairing-store 승인을 상속하지 **않습니다**. + 페어링은 DM 전용으로 유지됩니다. 그룹의 경우 `groupAllowFrom` 또는 그룹별/토픽별 `allowFrom`을 설정하세요. + `groupAllowFrom`이 설정되지 않은 경우 Telegram은 pairing store가 아니라 config `allowFrom`으로 폴백합니다. + 단일 소유자 bot의 실용적인 패턴: 사용자 ID를 `channels.telegram.allowFrom`에 설정하고, `groupAllowFrom`은 설정하지 않은 채 대상 그룹을 `channels.telegram.groups` 아래에서 허용합니다. + 런타임 참고: `channels.telegram`이 완전히 없으면, `channels.defaults.groupPolicy`가 명시적으로 설정되지 않는 한 런타임은 기본적으로 실패 시 닫힘 방식의 `groupPolicy="allowlist"`를 사용합니다. - 예시: 특정 그룹 하나의 모든 멤버 허용: + 예시: 특정 그룹 하나에서 모든 멤버 허용: ```json5 { @@ -193,7 +193,7 @@ curl "https://api.telegram.org/bot/getUpdates" } ``` - 예시: 특정 그룹 하나 안에서 특정 사용자만 허용: + 예시: 특정 그룹 하나에서 특정 사용자만 허용: ```json5 { @@ -211,11 +211,11 @@ curl "https://api.telegram.org/bot/getUpdates" ``` - 흔한 실수: `groupAllowFrom`은 Telegram 그룹 허용 목록이 아닙니다. + 흔한 실수: `groupAllowFrom`은 Telegram 그룹 allowlist가 아닙니다. - `-1001234567890` 같은 음수 Telegram 그룹 또는 슈퍼그룹 채팅 ID는 `channels.telegram.groups` 아래에 넣으세요. - - 허용된 그룹 안에서 어떤 사람이 봇을 트리거할 수 있는지 제한하려면 `8734062810` 같은 Telegram 사용자 ID를 `groupAllowFrom` 아래에 넣으세요. - - 허용된 그룹의 모든 멤버가 봇과 대화할 수 있게 하려는 경우에만 `groupAllowFrom: ["*"]`를 사용하세요. + - 허용된 그룹 안에서 어떤 사람이 bot을 트리거할 수 있는지 제한하려면 `8734062810` 같은 Telegram 사용자 ID를 `groupAllowFrom` 아래에 넣으세요. + - 허용된 그룹의 모든 멤버가 bot과 대화할 수 있게 하려는 경우에만 `groupAllowFrom: ["*"]`을 사용하세요. @@ -236,9 +236,9 @@ curl "https://api.telegram.org/bot/getUpdates" - `/activation always` - `/activation mention` - 이는 세션 상태만 업데이트합니다. 지속하려면 구성을 사용하세요. + 이 항목들은 세션 상태만 업데이트합니다. 지속성을 위해서는 config를 사용하세요. - 지속 구성 예시: + 지속 config 예시: ```json5 { @@ -255,7 +255,7 @@ curl "https://api.telegram.org/bot/getUpdates" 그룹 채팅 ID 가져오기: - 그룹 메시지를 `@userinfobot` / `@getidsbot`으로 전달 - - 또는 `openclaw logs --follow`에서 `chat.id` 읽기 + - 또는 `openclaw logs --follow`에서 `chat.id` 확인 - 또는 Bot API `getUpdates` 검사 @@ -265,31 +265,32 @@ curl "https://api.telegram.org/bot/getUpdates" - Telegram은 Gateway 프로세스가 소유합니다. - 라우팅은 결정적입니다. Telegram 인바운드는 Telegram으로 답장합니다(모델이 채널을 선택하지 않음). -- 인바운드 메시지는 답장 메타데이터와 미디어 플레이스홀더가 포함된 공유 채널 엔벌로프로 정규화됩니다. -- 그룹 세션은 그룹 ID로 격리됩니다. 포럼 주제는 주제를 격리하기 위해 `:topic:`를 추가합니다. -- DM 메시지는 `message_thread_id`를 포함할 수 있습니다. OpenClaw는 답장을 위해 스레드 ID를 보존하지만 기본적으로 DM은 플랫 세션에 유지합니다. 의도적으로 DM 주제 세션 격리를 원할 때는 `channels.telegram.dm.threadReplies: "inbound"`, `channels.telegram.direct..threadReplies: "inbound"`, `requireTopic: true` 또는 일치하는 주제 구성을 설정하세요. -- 롱 폴링은 채팅별/스레드별 순서를 적용하는 grammY runner를 사용합니다. 전체 runner sink 동시성은 `agents.defaults.maxConcurrent`를 사용합니다. -- 롱 폴링은 각 Gateway 프로세스 내부에서 보호되므로 한 번에 하나의 활성 poller만 봇 토큰을 사용할 수 있습니다. 그래도 `getUpdates` 409 충돌이 보인다면 다른 OpenClaw Gateway, 스크립트 또는 외부 poller가 같은 토큰을 사용 중일 가능성이 큽니다. -- 롱 폴링 watchdog 재시작은 기본적으로 완료된 `getUpdates` 활성 신호가 120초 동안 없을 때 트리거됩니다. 배포 환경에서 장시간 실행 작업 중 잘못된 polling-stall 재시작이 계속 발생하는 경우에만 `channels.telegram.pollingStallThresholdMs`를 늘리세요. 값은 밀리초 단위이며 `30000`부터 `600000`까지 허용됩니다. 계정별 오버라이드가 지원됩니다. +- 인바운드 메시지는 답장 메타데이터와 미디어 placeholder가 포함된 공유 채널 envelope로 정규화됩니다. +- 그룹 세션은 그룹 ID별로 격리됩니다. 포럼 토픽은 토픽을 격리하기 위해 `:topic:`를 덧붙입니다. +- DM 메시지는 `message_thread_id`를 포함할 수 있습니다. OpenClaw는 답장용 스레드 ID를 보존하지만, 기본적으로 DM은 평면 세션으로 유지합니다. DM 토픽 세션 격리를 의도적으로 원하는 경우 `channels.telegram.dm.threadReplies: "inbound"`, `channels.telegram.direct..threadReplies: "inbound"`, `requireTopic: true` 또는 일치하는 토픽 config를 구성하세요. +- Long polling은 채팅별/스레드별 순서를 보장하는 grammY runner를 사용합니다. 전체 runner sink 동시성은 `agents.defaults.maxConcurrent`를 사용합니다. +- Long polling은 각 Gateway 프로세스 내부에서 보호되어 한 번에 하나의 활성 poller만 bot 토큰을 사용할 수 있습니다. 여전히 `getUpdates` 409 충돌이 보인다면, 다른 OpenClaw Gateway, 스크립트 또는 외부 poller가 같은 토큰을 사용 중일 가능성이 큽니다. +- Long-polling watchdog 재시작은 기본적으로 완료된 `getUpdates` 활성 상태가 120초 동안 없으면 트리거됩니다. 장시간 실행 작업 중에도 배포 환경에서 잘못된 polling-stall 재시작이 계속 보이는 경우에만 `channels.telegram.pollingStallThresholdMs`를 늘리세요. 값은 밀리초 단위이며 `30000`부터 `600000`까지 허용됩니다. 계정별 재정의가 지원됩니다. - Telegram Bot API는 읽음 확인을 지원하지 않습니다(`sendReadReceipts`는 적용되지 않음). -## 기능 참고 +## 기능 참조 - + OpenClaw는 부분 답장을 실시간으로 스트리밍할 수 있습니다. - 직접 채팅: 미리보기 메시지 + `editMessageText` - - 그룹/주제: 미리보기 메시지 + `editMessageText` + - 그룹/토픽: 미리보기 메시지 + `editMessageText` 요구 사항: - `channels.telegram.streaming`은 `off | partial | block | progress`입니다(기본값: `partial`) - - `progress`는 편집 가능한 상태 초안 하나를 유지하고 최종 전달 전까지 도구 진행 상황으로 업데이트합니다. - - `streaming.preview.toolProgress`는 도구/진행 상황 업데이트가 같은 편집된 미리보기 메시지를 재사용할지 제어합니다(기본값: 미리보기 스트리밍이 활성화된 경우 `true`). - - 기존 `channels.telegram.streamMode`와 불리언 `streaming` 값은 감지됩니다. `openclaw doctor --fix`를 실행해 `channels.telegram.streaming.mode`로 마이그레이션하세요. + - `progress`는 편집 가능한 상태 초안 하나를 유지하고 최종 전달 전까지 도구 진행 상황으로 업데이트합니다 + - `streaming.preview.toolProgress`는 도구/진행 상황 업데이트가 동일한 편집된 미리보기 메시지를 재사용할지 제어합니다(미리보기 스트리밍이 활성일 때 기본값: `true`) + - `streaming.preview.commandText`는 해당 도구 진행 줄 내부의 command/exec 세부 정보를 제어합니다. `raw`(기본값, 릴리스된 동작 보존) 또는 `status`(도구 레이블만) + - 기존 `channels.telegram.streamMode` 및 boolean `streaming` 값은 감지됩니다. 이를 `channels.telegram.streaming.mode`로 마이그레이션하려면 `openclaw doctor --fix`를 실행하세요 - 도구 진행 상황 미리보기 업데이트는 도구가 실행되는 동안 표시되는 짧은 상태 줄입니다. 예를 들어 명령 실행, 파일 읽기, 계획 업데이트 또는 패치 요약이 있습니다. Telegram은 `v2026.4.22` 및 이후 버전의 릴리스된 OpenClaw 동작과 일치하도록 기본적으로 이를 활성화합니다. 답변 텍스트용 편집 미리보기는 유지하되 도구 진행 상황 줄을 숨기려면 다음을 설정하세요. + 도구 진행 미리보기 업데이트는 도구가 실행되는 동안 표시되는 짧은 상태 줄입니다. 예를 들어 명령 실행, 파일 읽기, 계획 업데이트 또는 패치 요약이 있습니다. Telegram은 `v2026.4.22` 및 이후 버전의 릴리스된 OpenClaw 동작과 일치하도록 기본적으로 이를 활성화합니다. 답변 텍스트용 편집 미리보기는 유지하되 도구 진행 줄을 숨기려면 다음과 같이 설정하세요. ```json { @@ -306,47 +307,82 @@ curl "https://api.telegram.org/bot/getUpdates" } ``` - `streaming.mode: "off"`는 최종 응답만 전달하려는 경우에만 사용하세요. Telegram 미리 보기 편집이 비활성화되고, 일반 도구/진행 상황 잡담은 독립 상태 메시지로 전송되는 대신 억제됩니다. 승인 프롬프트, 미디어 페이로드, 오류는 여전히 일반 최종 전달 경로를 통해 라우팅됩니다. 도구 진행 상태 줄은 숨기면서 답변 미리 보기 편집만 유지하려면 `streaming.preview.toolProgress: false`를 사용하세요. + 도구 진행은 표시하되 command/exec 텍스트는 숨기려면 다음과 같이 설정하세요. + + ```json + { + "channels": { + "telegram": { + "streaming": { + "mode": "partial", + "preview": { + "commandText": "status" + } + } + } + } + } + ``` + + 진행 초안 모드의 경우 동일한 명령 텍스트 정책을 `streaming.progress` 아래에 넣으세요. + + ```json + { + "channels": { + "telegram": { + "streaming": { + "mode": "progress", + "progress": { + "toolProgress": true, + "commandText": "status" + } + } + } + } + } + ``` + + 최종 응답만 전달하려는 경우에만 `streaming.mode: "off"`를 사용하세요. Telegram 미리보기 편집은 비활성화되고, 일반 도구/진행 잡담은 독립 상태 메시지로 전송되는 대신 억제됩니다. 승인 프롬프트, 미디어 페이로드, 오류는 여전히 일반 최종 전달 경로를 통해 라우팅됩니다. 도구 진행 상태 줄을 숨기면서 답변 미리보기 편집만 유지하려면 `streaming.preview.toolProgress: false`를 사용하세요. - Telegram 선택 인용 답장은 예외입니다. `replyToMode`가 `"first"`, `"all"` 또는 `"batched"`이고 인바운드 메시지에 선택된 인용 텍스트가 포함된 경우, OpenClaw는 답변 미리 보기를 편집하는 대신 Telegram의 네이티브 인용 답장 경로를 통해 최종 답변을 전송하므로 `streaming.preview.toolProgress`는 해당 턴의 짧은 상태 줄을 표시할 수 없습니다. 선택된 인용 텍스트가 없는 현재 메시지 답장은 계속 미리 보기 스트리밍을 유지합니다. 도구 진행 상황 표시가 네이티브 인용 답장보다 더 중요하면 `replyToMode: "off"`를 설정하거나, 이 절충을 인정하려면 `streaming.preview.toolProgress: false`를 설정하세요. + Telegram 선택 인용 답글은 예외입니다. `replyToMode`가 `"first"`, `"all"` 또는 `"batched"`이고 인바운드 메시지에 선택된 인용 텍스트가 포함된 경우, OpenClaw는 답변 미리보기를 편집하는 대신 Telegram의 네이티브 인용 답글 경로를 통해 최종 답변을 전송하므로, 해당 턴에서는 `streaming.preview.toolProgress`가 짧은 상태 줄을 표시할 수 없습니다. 선택된 인용 텍스트가 없는 현재 메시지 답글은 미리보기 스트리밍을 계속 유지합니다. 도구 진행 표시가 네이티브 인용 답글보다 더 중요하면 `replyToMode: "off"`를 설정하거나, 이 절충을 인정하려면 `streaming.preview.toolProgress: false`를 설정하세요. - 텍스트 전용 답장의 경우: + 텍스트 전용 답글의 경우: - - 짧은 DM/그룹/토픽 미리 보기: 미리 보기가 나타난 뒤 보이는 비미리 보기 메시지가 전송되지 않은 한, OpenClaw는 같은 미리 보기 메시지를 유지하고 제자리에서 최종 편집을 수행합니다. - - 미리 보기 뒤에 보이는 비미리 보기 출력이 이어지는 경우: OpenClaw는 완료된 답장을 새 최종 메시지로 전송하고 이전 미리 보기를 정리하므로, 최종 답변은 중간 출력 뒤에 표시됩니다. - - 약 1분보다 오래된 미리 보기: OpenClaw는 완료된 답장을 새 최종 메시지로 전송한 다음 미리 보기를 정리하므로, Telegram의 표시 타임스탬프는 미리 보기 생성 시간이 아니라 완료 시간을 반영합니다. + - 짧은 DM/그룹/토픽 미리보기: 미리보기가 나타난 뒤 표시되는 비미리보기 메시지가 전송되지 않았다면 OpenClaw는 동일한 미리보기 메시지를 유지하고 제자리에서 최종 편집을 수행합니다 + - 표시되는 비미리보기 출력 뒤의 미리보기: OpenClaw는 완료된 답글을 새 최종 메시지로 전송하고 이전 미리보기를 정리하므로, 최종 답변이 중간 출력 뒤에 나타납니다 + - 약 1분보다 오래된 미리보기: OpenClaw는 완료된 답글을 새 최종 메시지로 전송한 뒤 미리보기를 정리하므로, Telegram의 표시 타임스탬프가 미리보기 생성 시간이 아니라 완료 시간을 반영합니다 - 복잡한 답장(예: 미디어 페이로드)의 경우, OpenClaw는 일반 최종 전달 방식으로 폴백한 다음 미리 보기 메시지를 정리합니다. + 복잡한 답글(예: 미디어 페이로드)의 경우 OpenClaw는 일반 최종 전달로 대체한 뒤 미리보기 메시지를 정리합니다. - 미리 보기 스트리밍은 블록 스트리밍과 별개입니다. Telegram에서 블록 스트리밍이 명시적으로 활성화된 경우, OpenClaw는 이중 스트리밍을 피하기 위해 미리 보기 스트림을 건너뜁니다. + 미리보기 스트리밍은 블록 스트리밍과 별개입니다. Telegram에 대해 블록 스트리밍이 명시적으로 활성화되어 있으면 OpenClaw는 이중 스트리밍을 피하기 위해 미리보기 스트림을 건너뜁니다. Telegram 전용 추론 스트림: - - `/reasoning stream`은 생성 중 추론을 실시간 미리 보기에 전송합니다. - - 최종 전달 후 추론 미리 보기는 삭제됩니다. 추론을 계속 표시해야 하면 `/reasoning on`을 사용하세요. - - 최종 답변은 추론 텍스트 없이 전송됩니다. + - `/reasoning stream`은 생성 중 추론을 실시간 미리보기로 전송합니다 + - 추론 미리보기는 최종 전달 후 삭제됩니다. 추론을 계속 표시해야 하면 `/reasoning on`을 사용하세요 + - 최종 답변은 추론 텍스트 없이 전송됩니다 - + 아웃바운드 텍스트는 Telegram `parse_mode: "HTML"`을 사용합니다. - - Markdown과 유사한 텍스트는 Telegram에 안전한 HTML로 렌더링됩니다. + - Markdown 스타일 텍스트는 Telegram에 안전한 HTML로 렌더링됩니다. - 원시 모델 HTML은 Telegram 파싱 실패를 줄이기 위해 이스케이프됩니다. - Telegram이 파싱된 HTML을 거부하면 OpenClaw는 일반 텍스트로 다시 시도합니다. - 링크 미리 보기는 기본적으로 활성화되며 `channels.telegram.linkPreview: false`로 비활성화할 수 있습니다. + 링크 미리보기는 기본적으로 활성화되어 있으며 `channels.telegram.linkPreview: false`로 비활성화할 수 있습니다. - + Telegram 명령 메뉴 등록은 시작 시 `setMyCommands`로 처리됩니다. 네이티브 명령 기본값: - - `commands.native: "auto"`는 Telegram에서 네이티브 명령을 활성화합니다. + - `commands.native: "auto"`는 Telegram에 대해 네이티브 명령을 활성화합니다 사용자 지정 명령 메뉴 항목 추가: @@ -365,47 +401,47 @@ curl "https://api.telegram.org/bot/getUpdates" 규칙: - - 이름은 정규화됩니다(앞의 `/` 제거, 소문자화). + - 이름은 정규화됩니다(앞의 `/` 제거, 소문자화) - 유효한 패턴: `a-z`, `0-9`, `_`, 길이 `1..32` - - 사용자 지정 명령은 네이티브 명령을 재정의할 수 없습니다. - - 충돌/중복은 건너뛰고 기록됩니다. + - 사용자 지정 명령은 네이티브 명령을 재정의할 수 없습니다 + - 충돌/중복은 건너뛰고 로그에 기록됩니다 참고: - - 사용자 지정 명령은 메뉴 항목일 뿐이며, 동작을 자동으로 구현하지 않습니다. - - Plugin/skill 명령은 Telegram 메뉴에 표시되지 않더라도 입력하면 계속 작동할 수 있습니다. + - 사용자 지정 명령은 메뉴 항목일 뿐이며, 동작을 자동으로 구현하지 않습니다 + - Telegram 메뉴에 표시되지 않더라도 Plugin/Skills 명령은 입력 시 계속 작동할 수 있습니다 - 네이티브 명령이 비활성화된 경우, 기본 제공 명령은 제거됩니다. 구성된 경우 사용자 지정/Plugin 명령은 계속 등록될 수 있습니다. + 네이티브 명령이 비활성화되면 기본 제공 항목이 제거됩니다. 구성된 경우 사용자 지정/Plugin 명령은 여전히 등록될 수 있습니다. 일반적인 설정 실패: - - `BOT_COMMANDS_TOO_MUCH`와 함께 `setMyCommands failed`가 표시되면, 잘라낸 뒤에도 Telegram 메뉴가 여전히 넘쳤다는 뜻입니다. Plugin/skill/사용자 지정 명령을 줄이거나 `channels.telegram.commands.native`를 비활성화하세요. - - 직접 Bot API curl 명령은 작동하지만 `deleteWebhook`, `deleteMyCommands` 또는 `setMyCommands`가 `404: Not Found`로 실패하면 `channels.telegram.apiRoot`가 전체 `/bot` 엔드포인트로 설정되었을 수 있습니다. `apiRoot`는 Bot API 루트만이어야 하며, `openclaw doctor --fix`는 실수로 붙은 뒤쪽 `/bot`을 제거합니다. - - `getMe returned 401`은 Telegram이 구성된 봇 토큰을 거부했다는 뜻입니다. `botToken`, `tokenFile` 또는 `TELEGRAM_BOT_TOKEN`을 현재 BotFather 토큰으로 업데이트하세요. OpenClaw는 폴링 전에 중지하므로 이것은 Webhook 정리 실패로 보고되지 않습니다. - - 네트워크/페치 오류와 함께 `setMyCommands failed`가 표시되면 일반적으로 `api.telegram.org`로의 아웃바운드 DNS/HTTPS가 차단되었다는 뜻입니다. + - `BOT_COMMANDS_TOO_MUCH`와 함께 `setMyCommands failed`가 표시되면 정리 후에도 Telegram 메뉴가 여전히 초과되었다는 뜻입니다. Plugin/Skills/사용자 지정 명령을 줄이거나 `channels.telegram.commands.native`를 비활성화하세요. + - 직접 Bot API curl 명령은 작동하는데 `deleteWebhook`, `deleteMyCommands` 또는 `setMyCommands`가 `404: Not Found`로 실패하면 `channels.telegram.apiRoot`가 전체 `/bot` 엔드포인트로 설정되었을 수 있습니다. `apiRoot`는 Bot API 루트만이어야 하며, `openclaw doctor --fix`는 실수로 붙은 후행 `/bot`을 제거합니다. + - `getMe returned 401`은 Telegram이 구성된 봇 토큰을 거부했다는 뜻입니다. 현재 BotFather 토큰으로 `botToken`, `tokenFile` 또는 `TELEGRAM_BOT_TOKEN`을 업데이트하세요. OpenClaw는 폴링 전에 중지되므로 이는 Webhook 정리 실패로 보고되지 않습니다. + - 네트워크/fetch 오류와 함께 `setMyCommands failed`가 표시되면 일반적으로 `api.telegram.org`로 나가는 DNS/HTTPS가 차단되었다는 뜻입니다. ### 기기 페어링 명령(`device-pair` Plugin) `device-pair` Plugin이 설치된 경우: - 1. `/pair`가 설정 코드를 생성합니다. - 2. iOS 앱에 코드를 붙여넣습니다. - 3. `/pair pending`은 대기 중인 요청(역할/스코프 포함)을 나열합니다. - 4. 요청을 승인합니다. - - 명시적 승인은 `/pair approve ` + 1. `/pair`는 설정 코드를 생성합니다 + 2. iOS 앱에 코드를 붙여 넣습니다 + 3. `/pair pending`은 대기 중인 요청을 나열합니다(역할/범위 포함) + 4. 요청을 승인합니다: + - 명시적 승인에는 `/pair approve ` - 대기 중인 요청이 하나뿐이면 `/pair approve` - - 가장 최근 요청은 `/pair approve latest` + - 가장 최근 요청에는 `/pair approve latest` - 설정 코드는 수명이 짧은 부트스트랩 토큰을 포함합니다. 기본 제공 부트스트랩 인계는 기본 Node 토큰을 `scopes: []`로 유지합니다. 인계된 모든 운영자 토큰은 `operator.approvals`, `operator.read`, `operator.talk.secrets`, `operator.write`로 제한됩니다. 부트스트랩 스코프 검사는 역할 접두사를 사용하므로 해당 운영자 허용 목록은 운영자 요청만 충족합니다. 비운영자 역할은 여전히 자체 역할 접두사 아래의 스코프가 필요합니다. + 설정 코드는 수명이 짧은 부트스트랩 토큰을 전달합니다. 기본 제공 부트스트랩 인계는 기본 노드 토큰을 `scopes: []`로 유지합니다. 인계된 모든 운영자 토큰은 `operator.approvals`, `operator.read`, `operator.talk.secrets`, `operator.write`로 제한됩니다. 부트스트랩 범위 검사는 역할 접두사가 붙으므로, 해당 운영자 허용 목록은 운영자 요청만 충족합니다. 비운영자 역할은 여전히 자체 역할 접두사 아래의 범위가 필요합니다. - 기기가 변경된 인증 세부 정보(예: 역할/스코프/공개 키)로 다시 시도하면 이전 대기 요청은 대체되고 새 요청은 다른 `requestId`를 사용합니다. 승인하기 전에 `/pair pending`을 다시 실행하세요. + 기기가 변경된 인증 세부 정보(예: 역할/범위/공개 키)로 다시 시도하면 이전 대기 요청은 대체되고 새 요청은 다른 `requestId`를 사용합니다. 승인하기 전에 `/pair pending`을 다시 실행하세요. 자세한 내용: [페어링](/ko/channels/pairing#pair-via-telegram-recommended-for-ios). - - 인라인 키보드 스코프 구성: + + 인라인 키보드 범위 구성: ```json5 { @@ -437,7 +473,7 @@ curl "https://api.telegram.org/bot/getUpdates" } ``` - 스코프: + 범위: - `off` - `dm` @@ -445,9 +481,9 @@ curl "https://api.telegram.org/bot/getUpdates" - `all` - `allowlist`(기본값) - 레거시 `capabilities: ["inlineButtons"]`는 `inlineButtons: "all"`로 매핑됩니다. + 기존 `capabilities: ["inlineButtons"]`는 `inlineButtons: "all"`로 매핑됩니다. - 메시지 작업 예시: + 메시지 동작 예시: ```json5 { @@ -465,13 +501,13 @@ curl "https://api.telegram.org/bot/getUpdates" } ``` - 콜백 클릭은 텍스트로 에이전트에 전달됩니다. + 콜백 클릭은 텍스트로 에이전트에 전달됩니다: `callback_data: ` - - Telegram 도구 작업에는 다음이 포함됩니다. + + Telegram 도구 동작에는 다음이 포함됩니다: - `sendMessage`(`to`, `content`, 선택 사항 `mediaUrl`, `replyToMessageId`, `messageThreadId`) - `react`(`chatId`, `messageId`, `emoji`) @@ -479,9 +515,9 @@ curl "https://api.telegram.org/bot/getUpdates" - `editMessage`(`chatId`, `messageId`, `content`) - `createForumTopic`(`chatId`, `name`, 선택 사항 `iconColor`, `iconCustomEmojiId`) - 채널 메시지 작업은 인체공학적인 별칭(`send`, `react`, `delete`, `edit`, `sticker`, `sticker-search`, `topic-create`)을 노출합니다. + 채널 메시지 동작은 사용하기 쉬운 별칭(`send`, `react`, `delete`, `edit`, `sticker`, `sticker-search`, `topic-create`)을 노출합니다. - 게이트 제어: + 게이팅 제어: - `channels.telegram.actions.sendMessage` - `channels.telegram.actions.deleteMessage` @@ -489,47 +525,47 @@ curl "https://api.telegram.org/bot/getUpdates" - `channels.telegram.actions.sticker`(기본값: 비활성화) 참고: `edit`와 `topic-create`는 현재 기본적으로 활성화되어 있으며 별도의 `channels.telegram.actions.*` 토글이 없습니다. - 런타임 전송은 활성 구성/시크릿 스냅샷(시작/다시 로드)을 사용하므로 작업 경로는 전송마다 임시 SecretRef 재해석을 수행하지 않습니다. + 런타임 전송은 활성 구성/비밀 스냅샷(시작/다시 로드)을 사용하므로, 동작 경로는 전송마다 임시 SecretRef 재해석을 수행하지 않습니다. 반응 제거 의미 체계: [/tools/reactions](/ko/tools/reactions) - - Telegram은 생성된 출력에서 명시적 답장 스레딩 태그를 지원합니다. + + Telegram은 생성된 출력에서 명시적 답글 스레딩 태그를 지원합니다: - - `[[reply_to_current]]`는 트리거한 메시지에 답장합니다. - - `[[reply_to:]]`는 특정 Telegram 메시지 ID에 답장합니다. + - `[[reply_to_current]]`는 트리거한 메시지에 답글을 답니다 + - `[[reply_to:]]`는 특정 Telegram 메시지 ID에 답글을 답니다 - `channels.telegram.replyToMode`는 처리를 제어합니다. + `channels.telegram.replyToMode`는 처리를 제어합니다: - `off`(기본값) - `first` - `all` - 답장 스레딩이 활성화되어 있고 원본 Telegram 텍스트 또는 캡션을 사용할 수 있으면, OpenClaw는 네이티브 Telegram 인용 발췌를 자동으로 포함합니다. Telegram은 네이티브 인용 텍스트를 1024 UTF-16 코드 단위로 제한하므로 더 긴 메시지는 시작 부분부터 인용되고 Telegram이 인용을 거부하면 일반 답장으로 폴백합니다. + 답글 스레딩이 활성화되어 있고 원래 Telegram 텍스트 또는 캡션을 사용할 수 있으면 OpenClaw는 네이티브 Telegram 인용 발췌를 자동으로 포함합니다. Telegram은 네이티브 인용 텍스트를 1024 UTF-16 코드 단위로 제한하므로, 더 긴 메시지는 시작 부분부터 인용되며 Telegram이 인용을 거부하면 일반 답글로 대체됩니다. - 참고: `off`는 암시적 답장 스레딩을 비활성화합니다. 명시적 `[[reply_to_*]]` 태그는 여전히 적용됩니다. + 참고: `off`는 암시적 답글 스레딩을 비활성화합니다. 명시적 `[[reply_to_*]]` 태그는 여전히 적용됩니다. - + 포럼 슈퍼그룹: - - 토픽 세션 키는 `:topic:`를 추가합니다. - - 답장과 입력 중 표시는 토픽 스레드를 대상으로 합니다. + - 토픽 세션 키는 `:topic:`를 덧붙입니다 + - 답글 및 입력 상태는 토픽 스레드를 대상으로 합니다 - 토픽 구성 경로: `channels.telegram.groups..topics.` 일반 토픽(`threadId=1`) 특수 사례: - - 메시지 전송은 `message_thread_id`를 생략합니다(Telegram은 `sendMessage(...thread_id=1)`를 거부함). - - 입력 중 작업은 여전히 `message_thread_id`를 포함합니다. + - 메시지 전송은 `message_thread_id`를 생략합니다(Telegram은 `sendMessage(...thread_id=1)`을 거부합니다) + - 입력 동작은 여전히 `message_thread_id`를 포함합니다 - 토픽 상속: 토픽 항목은 재정의되지 않는 한 그룹 설정(`requireMention`, `allowFrom`, `skills`, `systemPrompt`, `enabled`, `groupPolicy`)을 상속합니다. + 토픽 상속: 토픽 항목은 재정의되지 않는 한 그룹 설정을 상속합니다(`requireMention`, `allowFrom`, `skills`, `systemPrompt`, `enabled`, `groupPolicy`). `agentId`는 토픽 전용이며 그룹 기본값에서 상속되지 않습니다. - **토픽별 에이전트 라우팅**: 각 토픽은 토픽 구성에서 `agentId`를 설정하여 다른 에이전트로 라우팅할 수 있습니다. 이렇게 하면 각 토픽은 자체 격리된 워크스페이스, 메모리, 세션을 갖습니다. 예시: + **토픽별 에이전트 라우팅**: 각 토픽은 토픽 구성에서 `agentId`를 설정하여 다른 에이전트로 라우팅할 수 있습니다. 이렇게 하면 각 토픽에 고유한 격리된 작업 공간, 메모리, 세션이 제공됩니다. 예시: ```json5 { @@ -549,26 +585,28 @@ curl "https://api.telegram.org/bot/getUpdates" } ``` - 그러면 각 토픽은 자체 세션 키를 갖습니다. `agent:zu:telegram:group:-1001234567890:topic:3` + 그러면 각 토픽에는 고유한 세션 키가 있습니다: `agent:zu:telegram:group:-1001234567890:topic:3` - **영구 ACP 토픽 바인딩**: 포럼 토픽은 최상위 타입 지정 ACP 바인딩(`type: "acp"`와 `match.channel: "telegram"`, `peer.kind: "group"` 및 `-1001234567890:topic:42` 같은 토픽 한정 ID를 포함하는 `bindings[]`)을 통해 ACP 하네스 세션을 고정할 수 있습니다. 현재 그룹/슈퍼그룹의 포럼 토픽으로 범위가 제한됩니다. [ACP 에이전트](/ko/tools/acp-agents)를 참조하세요. + **영구 ACP 토픽 바인딩**: 포럼 토픽은 최상위 형식화 ACP 바인딩(`type: "acp"` 및 `match.channel: "telegram"`, `peer.kind: "group"`, 그리고 `-1001234567890:topic:42`와 같은 토픽 한정 ID가 포함된 `bindings[]`)을 통해 ACP 하네스 세션을 고정할 수 있습니다. 현재 그룹/슈퍼그룹의 포럼 토픽으로 범위가 지정됩니다. [ACP 에이전트](/ko/tools/acp-agents)를 참조하세요. - **채팅에서 스레드 바인딩 ACP 생성**: `/acp spawn --thread here|auto`는 현재 토픽을 새 ACP 세션에 바인딩합니다. 후속 메시지는 해당 세션으로 직접 라우팅됩니다. OpenClaw는 생성 확인을 토픽 안에 고정합니다. `channels.telegram.threadBindings.spawnSessions`가 계속 활성화되어 있어야 합니다(기본값: `true`). + **채팅에서 스레드 바인딩 ACP 생성**: `/acp spawn --thread here|auto`는 현재 토픽을 새 ACP 세션에 바인딩합니다. 후속 메시지는 그곳으로 직접 라우팅됩니다. OpenClaw는 생성 확인을 토픽 안에 고정합니다. `channels.telegram.threadBindings.spawnSessions`가 활성화된 상태로 유지되어야 합니다(기본값: `true`). - 템플릿 컨텍스트는 `MessageThreadId`와 `IsForum`을 노출합니다. `message_thread_id`가 있는 DM 채팅은 기본적으로 플랫 세션에서 DM 라우팅과 답장 메타데이터를 유지합니다. `threadReplies: "inbound"`, `threadReplies: "always"`, `requireTopic: true` 또는 일치하는 토픽 구성으로 설정된 경우에만 스레드 인식 세션 키를 사용합니다. 계정 기본값에는 최상위 `channels.telegram.dm.threadReplies`를 사용하거나, 단일 DM에는 `direct..threadReplies`를 사용하세요. + 템플릿 컨텍스트는 `MessageThreadId`와 `IsForum`을 노출합니다. `message_thread_id`가 있는 DM 채팅은 기본적으로 플랫 세션에서 DM 라우팅과 답장 메타데이터를 유지합니다. `threadReplies: "inbound"`, `threadReplies: "always"`, `requireTopic: true` 또는 일치하는 토픽 설정으로 구성된 경우에만 스레드를 인식하는 세션 키를 사용합니다. 계정 기본값에는 최상위 `channels.telegram.dm.threadReplies`를 사용하고, 특정 DM 하나에는 `direct..threadReplies`를 사용하세요. - + ### 오디오 메시지 Telegram은 음성 메모와 오디오 파일을 구분합니다. - 기본값: 오디오 파일 동작 - - 음성 메모 전송을 강제하려면 에이전트 답장에 `[[audio_as_voice]]` 태그를 사용하세요. - - 인바운드 음성 메모 전사는 에이전트 컨텍스트에서 기계 생성의 신뢰할 수 없는 텍스트로 프레이밍됩니다. 멘션 감지는 여전히 원시 전사를 사용하므로 멘션 게이트 음성 메시지는 계속 작동합니다. + - 에이전트 답장에 태그 `[[audio_as_voice]]`를 넣으면 음성 메모 전송을 강제합니다. + - 수신 음성 메모의 전사는 에이전트 컨텍스트에서 기계 생성, + 신뢰할 수 없는 텍스트로 구성됩니다. 멘션 감지는 여전히 원시 + 전사를 사용하므로 멘션으로 제한된 음성 메시지는 계속 작동합니다. - 메시지 작업 예시: + 메시지 액션 예시: ```json5 { @@ -580,11 +618,11 @@ curl "https://api.telegram.org/bot/getUpdates" } ``` - ### 동영상 메시지 + ### 비디오 메시지 - Telegram은 동영상 파일과 동영상 노트를 구분합니다. + Telegram은 비디오 파일과 비디오 메모를 구분합니다. - 메시지 작업 예시: + 메시지 액션 예시: ```json5 { @@ -596,15 +634,15 @@ curl "https://api.telegram.org/bot/getUpdates" } ``` - 동영상 노트는 캡션을 지원하지 않으며, 제공된 메시지 텍스트는 별도로 전송됩니다. + 비디오 메모는 캡션을 지원하지 않습니다. 제공된 메시지 텍스트는 별도로 전송됩니다. ### 스티커 - 인바운드 스티커 처리: + 수신 스티커 처리: - - 정적 WEBP: 다운로드 및 처리됨(자리표시자 ``) + - 정적 WEBP: 다운로드 후 처리됨(플레이스홀더 ``) - 애니메이션 TGS: 건너뜀 - - 동영상 WEBM: 건너뜀 + - 비디오 WEBM: 건너뜀 스티커 컨텍스트 필드: @@ -618,9 +656,9 @@ curl "https://api.telegram.org/bot/getUpdates" - `~/.openclaw/telegram/sticker-cache.json` - 스티커는 가능한 경우 한 번 설명되고, 반복되는 비전 호출을 줄이기 위해 캐시됩니다. + 스티커는 한 번 설명되고(가능한 경우) 반복적인 비전 호출을 줄이기 위해 캐시됩니다. - 스티커 작업 활성화: + 스티커 액션 활성화: ```json5 { @@ -634,7 +672,7 @@ curl "https://api.telegram.org/bot/getUpdates" } ``` - 스티커 작업 전송: + 스티커 액션 전송: ```json5 { @@ -659,7 +697,7 @@ curl "https://api.telegram.org/bot/getUpdates" - Telegram 반응은 `message_reaction` 업데이트로 도착합니다(메시지 페이로드와 별도). + Telegram 반응은 `message_reaction` 업데이트로 도착합니다(메시지 페이로드와 별개). 활성화되면 OpenClaw는 다음과 같은 시스템 이벤트를 큐에 넣습니다. @@ -672,25 +710,25 @@ curl "https://api.telegram.org/bot/getUpdates" 참고: - - `own`은 봇이 보낸 메시지에 대한 사용자 반응만 의미합니다(전송 메시지 캐시를 통한 최선의 처리). - - 반응 이벤트는 여전히 Telegram 접근 제어(`dmPolicy`, `allowFrom`, `groupPolicy`, `groupAllowFrom`)를 따르며, 권한이 없는 발신자는 제외됩니다. + - `own`은 봇이 보낸 메시지에 대한 사용자 반응만 의미합니다(보낸 메시지 캐시를 통한 최선 노력). + - 반응 이벤트도 Telegram 접근 제어(`dmPolicy`, `allowFrom`, `groupPolicy`, `groupAllowFrom`)를 따릅니다. 권한이 없는 발신자는 삭제됩니다. - Telegram은 반응 업데이트에서 스레드 ID를 제공하지 않습니다. - - 포럼이 아닌 그룹은 그룹 채팅 세션으로 라우팅됩니다. + - 비포럼 그룹은 그룹 채팅 세션으로 라우팅됩니다. - 포럼 그룹은 정확한 원래 토픽이 아니라 그룹 일반 토픽 세션(`:topic:1`)으로 라우팅됩니다. 폴링/Webhook용 `allowed_updates`에는 `message_reaction`이 자동으로 포함됩니다. - - `ackReaction`은 OpenClaw가 인바운드 메시지를 처리하는 동안 확인 이모지를 보냅니다. + + `ackReaction`은 OpenClaw가 수신 메시지를 처리하는 동안 확인 이모지를 보냅니다. - 확인 순서: + 해석 순서: - `channels.telegram.accounts..ackReaction` - `channels.telegram.ackReaction` - `messages.ackReaction` - - 에이전트 ID 이모지 폴백(`agents.list[].identity.emoji`, 없으면 "👀") + - 에이전트 ID 이모지 대체값(`agents.list[].identity.emoji`, 없으면 "👀") 참고: @@ -699,7 +737,7 @@ curl "https://api.telegram.org/bot/getUpdates" - + 채널 설정 쓰기는 기본적으로 활성화되어 있습니다(`configWrites !== false`). Telegram으로 트리거되는 쓰기에는 다음이 포함됩니다. @@ -722,29 +760,29 @@ curl "https://api.telegram.org/bot/getUpdates" - 기본값은 롱 폴링입니다. Webhook 모드의 경우 `channels.telegram.webhookUrl` 및 `channels.telegram.webhookSecret`을 설정하세요. 선택 사항은 `webhookPath`, `webhookHost`, `webhookPort`입니다(기본값 `/telegram-webhook`, `127.0.0.1`, `8787`). + 기본값은 롱 폴링입니다. Webhook 모드의 경우 `channels.telegram.webhookUrl`과 `channels.telegram.webhookSecret`을 설정하세요. 선택 사항으로 `webhookPath`, `webhookHost`, `webhookPort`가 있습니다(기본값 `/telegram-webhook`, `127.0.0.1`, `8787`). 로컬 리스너는 `127.0.0.1:8787`에 바인딩됩니다. 공개 인그레스의 경우 로컬 포트 앞에 리버스 프록시를 두거나 의도적으로 `webhookHost: "0.0.0.0"`을 설정하세요. Webhook 모드는 Telegram에 `200`을 반환하기 전에 요청 가드, Telegram 비밀 토큰, JSON 본문을 검증합니다. - 그런 다음 OpenClaw는 롱 폴링에서 사용하는 것과 동일한 채팅별/토픽별 봇 레인을 통해 업데이트를 비동기적으로 처리하므로, 느린 에이전트 턴이 Telegram의 전달 ACK를 붙잡지 않습니다. + 그런 다음 OpenClaw는 롱 폴링에서 사용하는 동일한 채팅별/토픽별 봇 레인을 통해 업데이트를 비동기적으로 처리하므로 느린 에이전트 턴이 Telegram의 전달 ACK를 붙잡지 않습니다. - + - `channels.telegram.textChunkLimit` 기본값은 4000입니다. - - `channels.telegram.chunkMode="newline"`은 길이 분할 전에 단락 경계(빈 줄)를 우선합니다. - - `channels.telegram.mediaMaxMb`(기본값 100)는 인바운드 및 아웃바운드 Telegram 미디어 크기를 제한합니다. - - `channels.telegram.mediaGroupFlushMs`(기본값 500)는 OpenClaw가 Telegram 앨범/미디어 그룹을 하나의 인바운드 메시지로 디스패치하기 전에 버퍼링하는 시간을 제어합니다. 앨범 일부가 늦게 도착하면 늘리고, 앨범 답장 지연 시간을 줄이려면 줄이세요. - - `channels.telegram.timeoutSeconds`는 Telegram API 클라이언트 타임아웃을 재정의합니다(설정하지 않으면 grammY 기본값 적용). 봇 클라이언트는 설정된 값을 60초 아웃바운드 텍스트/타이핑 요청 가드보다 낮게 제한하여, OpenClaw의 전송 가드와 폴백이 실행되기 전에 grammY가 표시 가능한 답장 전달을 중단하지 않도록 합니다. 롱 폴링은 여전히 45초 `getUpdates` 요청 가드를 사용하므로 유휴 폴이 무기한 방치되지 않습니다. - - `channels.telegram.pollingStallThresholdMs`의 기본값은 `120000`입니다. 거짓 양성 폴링 정지 재시작에 대해서만 `30000`에서 `600000` 사이로 조정하세요. - - 그룹 컨텍스트 기록은 `channels.telegram.historyLimit` 또는 `messages.groupChat.historyLimit`(기본값 50)을 사용합니다. `0`은 비활성화합니다. + - `channels.telegram.chunkMode="newline"`은 길이 분할 전에 문단 경계(빈 줄)를 우선합니다. + - `channels.telegram.mediaMaxMb`(기본값 100)는 수신 및 발신 Telegram 미디어 크기를 제한합니다. + - `channels.telegram.mediaGroupFlushMs`(기본값 500)는 OpenClaw가 Telegram 앨범/미디어 그룹을 하나의 수신 메시지로 디스패치하기 전에 버퍼링하는 시간을 제어합니다. 앨범 일부가 늦게 도착하면 늘리고, 앨범 답장 지연 시간을 줄이려면 줄이세요. + - `channels.telegram.timeoutSeconds`는 Telegram API 클라이언트 타임아웃을 재정의합니다(설정하지 않으면 grammY 기본값 적용). 봇 클라이언트는 구성된 값이 60초 발신 텍스트/입력 중 요청 가드보다 낮으면 해당 가드 아래로 제한하여, OpenClaw의 전송 가드와 대체 처리가 실행되기 전에 grammY가 표시되는 답장 전달을 중단하지 않도록 합니다. 롱 폴링은 여전히 45초 `getUpdates` 요청 가드를 사용하므로 유휴 폴이 무기한 방치되지 않습니다. + - `channels.telegram.pollingStallThresholdMs` 기본값은 `120000`입니다. 잘못된 긍정 폴링 정지 재시작에 대해서만 `30000`에서 `600000` 사이로 조정하세요. + - 그룹 컨텍스트 기록은 `channels.telegram.historyLimit` 또는 `messages.groupChat.historyLimit`를 사용합니다(기본값 50). `0`은 비활성화합니다. - 답장/인용/전달 보조 컨텍스트는 현재 수신된 그대로 전달됩니다. - - Telegram 허용 목록은 주로 누가 에이전트를 트리거할 수 있는지를 제한하며, 완전한 보조 컨텍스트 삭제 경계는 아닙니다. + - Telegram 허용 목록은 주로 에이전트를 트리거할 수 있는 사람을 제어하며, 전체 보조 컨텍스트 삭제 경계가 아닙니다. - DM 기록 제어: - `channels.telegram.dmHistoryLimit` - `channels.telegram.dms[""].historyLimit` - - `channels.telegram.retry` 설정은 복구 가능한 아웃바운드 API 오류에 대해 Telegram 전송 헬퍼(CLI/도구/작업)에 적용됩니다. 인바운드 최종 답장 전달도 Telegram 사전 연결 실패에 대해 제한된 안전 전송 재시도를 사용하지만, 표시 메시지를 중복할 수 있는 모호한 전송 후 네트워크 엔벌로프는 재시도하지 않습니다. + - `channels.telegram.retry` 설정은 복구 가능한 발신 API 오류에 대해 Telegram 전송 헬퍼(CLI/도구/액션)에 적용됩니다. 수신 최종 답장 전달도 Telegram 연결 전 실패에 대해 제한된 안전 전송 재시도를 사용하지만, 표시되는 메시지를 중복할 수 있는 모호한 전송 후 네트워크 봉투는 재시도하지 않습니다. CLI 전송 대상은 숫자 채팅 ID 또는 사용자 이름일 수 있습니다. @@ -774,32 +812,32 @@ openclaw message poll --channel telegram --target -1001234567890:topic:42 \ - `channels.telegram.capabilities.inlineButtons`가 허용하는 경우 인라인 키보드용 `buttons` 블록과 함께 `--presentation` - 봇이 해당 채팅에서 고정할 수 있을 때 고정 전달을 요청하는 `--pin` 또는 `--delivery '{"pin":true}'` - - 아웃바운드 이미지 및 GIF를 압축 사진 또는 애니메이션 미디어 업로드 대신 문서로 보내는 `--force-document` + - 발신 이미지와 GIF를 압축된 사진 또는 애니메이션 미디어 업로드 대신 문서로 보내는 `--force-document` - 작업 게이팅: + 액션 게이팅: - - `channels.telegram.actions.sendMessage=false`는 폴을 포함한 아웃바운드 Telegram 메시지를 비활성화합니다. - - `channels.telegram.actions.poll=false`는 일반 전송은 활성 상태로 둔 채 Telegram 폴 생성을 비활성화합니다. + - `channels.telegram.actions.sendMessage=false`는 폴을 포함해 발신 Telegram 메시지를 비활성화합니다. + - `channels.telegram.actions.poll=false`는 일반 전송을 활성화한 채 Telegram 폴 생성을 비활성화합니다. - - Telegram은 승인자 DM에서 실행 승인을 지원하며, 선택적으로 원래 채팅 또는 토픽에 프롬프트를 게시할 수 있습니다. 승인자는 숫자 Telegram 사용자 ID여야 합니다. + + Telegram은 승인자 DM에서 exec 승인을 지원하며, 선택적으로 원래 채팅이나 토픽에 프롬프트를 게시할 수 있습니다. 승인자는 숫자 Telegram 사용자 ID여야 합니다. 설정 경로: - `channels.telegram.execApprovals.enabled`(확인 가능한 승인자가 하나 이상 있으면 자동 활성화) - - `channels.telegram.execApprovals.approvers`(`commands.ownerAllowFrom`의 숫자 소유자 ID로 폴백) + - `channels.telegram.execApprovals.approvers`(`commands.ownerAllowFrom`의 숫자 소유자 ID로 대체) - `channels.telegram.execApprovals.target`: `dm`(기본값) | `channel` | `both` - `agentFilter`, `sessionFilter` - `channels.telegram.allowFrom`, `groupAllowFrom`, `defaultTo`는 누가 봇과 대화할 수 있는지와 봇이 일반 답장을 어디로 보내는지를 제어합니다. 이것들이 누군가를 실행 승인자로 만들지는 않습니다. 아직 명령 소유자가 없을 때 첫 번째 승인된 DM 페어링은 `commands.ownerAllowFrom`을 부트스트랩하므로, 단일 소유자 설정은 `execApprovals.approvers` 아래에 ID를 중복하지 않아도 계속 작동합니다. + `channels.telegram.allowFrom`, `groupAllowFrom`, `defaultTo`는 봇과 대화할 수 있는 사람과 일반 답장을 보내는 위치를 제어합니다. 이것들이 누군가를 exec 승인자로 만들지는 않습니다. 아직 명령 소유자가 없을 때 처음 승인된 DM 페어링은 `commands.ownerAllowFrom`을 부트스트랩하므로, 단일 소유자 설정은 `execApprovals.approvers` 아래에 ID를 중복하지 않아도 계속 작동합니다. - 채널 전달은 채팅에 명령 텍스트를 표시합니다. 신뢰할 수 있는 그룹/토픽에서만 `channel` 또는 `both`를 활성화하세요. 프롬프트가 포럼 토픽에 도착하면 OpenClaw는 승인 프롬프트와 후속 조치에 해당 토픽을 유지합니다. 실행 승인은 기본적으로 30분 후 만료됩니다. + 채널 전달은 채팅에 명령 텍스트를 표시합니다. 신뢰할 수 있는 그룹/토픽에서만 `channel` 또는 `both`를 활성화하세요. 프롬프트가 포럼 토픽에 도착하면 OpenClaw는 승인 프롬프트와 후속 작업에 해당 토픽을 보존합니다. Exec 승인은 기본적으로 30분 후 만료됩니다. - 인라인 승인 버튼도 `channels.telegram.capabilities.inlineButtons`가 대상 표면(`dm`, `group` 또는 `all`)을 허용해야 합니다. `plugin:` 접두사가 붙은 승인 ID는 Plugin 승인을 통해 확인되고, 그 외에는 먼저 실행 승인을 통해 확인됩니다. + 인라인 승인 버튼도 대상 표면(`dm`, `group` 또는 `all`)을 허용하려면 `channels.telegram.capabilities.inlineButtons`가 필요합니다. `plugin:` 접두사가 있는 승인 ID는 Plugin 승인을 통해 해석되고, 그 외에는 exec 승인을 먼저 통해 해석됩니다. - [실행 승인](/ko/tools/exec-approvals)을 참조하세요. + [Exec 승인](/ko/tools/exec-approvals)을 참조하세요. @@ -808,10 +846,10 @@ openclaw message poll --channel telegram --target -1001234567890:topic:42 \ 에이전트가 전달 또는 제공자 오류를 만나면 Telegram은 오류 텍스트로 답장하거나 이를 억제할 수 있습니다. 두 설정 키가 이 동작을 제어합니다. -| 키 | 값 | 기본값 | 설명 | -| ----------------------------------- | ----------------- | ------- | ----------------------------------------------------------------------------------------------- | -| `channels.telegram.errorPolicy` | `reply`, `silent` | `reply` | `reply`는 채팅에 친절한 오류 메시지를 보냅니다. `silent`는 오류 답장을 완전히 억제합니다. | -| `channels.telegram.errorCooldownMs` | 숫자(ms) | `60000` | 같은 채팅에 대한 오류 답장 간 최소 시간입니다. 장애 중 오류 스팸을 방지합니다. | +| 키 | 값 | 기본값 | 설명 | +| ----------------------------------- | ----------------- | ------- | ------------------------------------------------------------------------------------------------- | +| `channels.telegram.errorPolicy` | `reply`, `silent` | `reply` | `reply`는 채팅에 친근한 오류 메시지를 보냅니다. `silent`는 오류 답장을 완전히 억제합니다. | +| `channels.telegram.errorCooldownMs` | 숫자(ms) | `60000` | 같은 채팅에 보내는 오류 답장 사이의 최소 시간입니다. 장애 중 오류 스팸을 방지합니다. | 계정별, 그룹별, 토픽별 재정의가 지원됩니다(다른 Telegram 설정 키와 동일한 상속). @@ -823,7 +861,7 @@ openclaw message poll --channel telegram --target -1001234567890:topic:42 \ errorCooldownMs: 120000, groups: { "-1001234567890": { - errorPolicy: "silent", // 이 그룹에서 오류 억제 + errorPolicy: "silent", // suppress errors in this group }, }, }, @@ -834,56 +872,56 @@ openclaw message poll --channel telegram --target -1001234567890:topic:42 \ ## 문제 해결 - + - - `requireMention=false`인 경우 Telegram 개인정보 보호 모드가 전체 가시성을 허용해야 합니다. - - BotFather: `/setprivacy` -> 비활성화 - - 그런 다음 그룹에서 봇을 제거하고 다시 추가 - - 설정이 멘션 없는 그룹 메시지를 기대할 때 `openclaw channels status`가 경고합니다. - - `openclaw channels status --probe`는 명시적인 숫자 그룹 ID를 확인할 수 있습니다. 와일드카드 `"*"`는 멤버십 프로브가 불가능합니다. + - `requireMention=false`인 경우 Telegram 개인정보 보호 모드는 전체 가시성을 허용해야 합니다. + - BotFather: `/setprivacy` -> Disable + - 그런 다음 그룹에서 봇을 제거하고 다시 추가하세요. + - 설정이 멘션되지 않은 그룹 메시지를 기대할 때 `openclaw channels status`가 경고합니다. + - `openclaw channels status --probe`는 명시적인 숫자 그룹 ID를 확인할 수 있습니다. 와일드카드 `"*"`는 멤버십 프로브를 할 수 없습니다. - 빠른 세션 테스트: `/activation always`. - - `channels.telegram.groups`가 있으면 그룹이 목록에 있어야 합니다(또는 `"*"` 포함). - - 그룹 내 봇 멤버십 확인 - - 건너뛰기 이유는 로그 검토: `openclaw logs --follow` + - `channels.telegram.groups`가 있으면 그룹이 목록에 있어야 합니다(또는 `"*"` 포함) + - 그룹의 봇 멤버십 확인 + - 건너뛰기 사유를 보려면 로그 검토: `openclaw logs --follow` - - 발신자 ID 승인(페어링 및/또는 숫자 `allowFrom`) - - 그룹 정책이 `open`이어도 명령 권한 부여는 계속 적용됩니다. - - `BOT_COMMANDS_TOO_MUCH`와 함께 `setMyCommands failed`가 표시되면 네이티브 메뉴에 항목이 너무 많다는 뜻입니다. Plugin/Skill/사용자 지정 명령을 줄이거나 네이티브 메뉴를 비활성화하세요. - - `deleteMyCommands` / `setMyCommands` 시작 호출과 `sendChatAction` 타이핑 호출은 제한되며 요청 타임아웃 시 Telegram의 전송 폴백을 통해 한 번 재시도됩니다. 지속적인 네트워크/페치 오류는 보통 `api.telegram.org`에 대한 DNS/HTTPS 도달성 문제를 나타냅니다. + - 발신자 ID 승인(페어링 및/또는 숫자형 `allowFrom`) + - 그룹 정책이 `open`이어도 명령 승인은 계속 적용됨 + - `BOT_COMMANDS_TOO_MUCH`와 함께 `setMyCommands failed`가 발생하면 네이티브 메뉴 항목이 너무 많다는 뜻입니다. Plugin/스킬/사용자 지정 명령을 줄이거나 네이티브 메뉴를 비활성화하세요. + - 시작 시 `deleteMyCommands` / `setMyCommands` 호출과 `sendChatAction` 입력 표시 호출은 제한 시간이 정해져 있으며, 요청 제한 시간 초과 시 Telegram의 전송 대체 경로를 통해 한 번 재시도합니다. 지속적인 네트워크/가져오기 오류는 보통 `api.telegram.org`에 대한 DNS/HTTPS 도달성 문제를 나타냅니다. - + - `getMe returned 401`은 구성된 봇 토큰에 대한 Telegram 인증 실패입니다. - BotFather에서 봇 토큰을 다시 복사하거나 재생성한 다음, 기본 계정의 `channels.telegram.botToken`, `channels.telegram.tokenFile`, `channels.telegram.accounts..botToken` 또는 `TELEGRAM_BOT_TOKEN`을 업데이트하세요. - - 시작 중 `deleteWebhook 401 Unauthorized`도 인증 실패입니다. 이를 "webhook이 없음"으로 처리하면 동일한 잘못된 토큰 실패가 이후 API 호출로 미뤄질 뿐입니다. + - 시작 중 `deleteWebhook 401 Unauthorized`도 인증 실패입니다. 이를 "Webhook이 없음"으로 처리하면 동일한 잘못된 토큰 실패가 이후 API 호출로 지연될 뿐입니다. - - Node 22+와 사용자 지정 fetch/proxy는 AbortSignal 타입이 일치하지 않으면 즉시 중단 동작을 유발할 수 있습니다. - - 일부 호스트는 `api.telegram.org`를 IPv6로 먼저 확인합니다. IPv6 송신이 손상되어 있으면 간헐적인 Telegram API 실패가 발생할 수 있습니다. - - 로그에 `TypeError: fetch failed` 또는 `Network request for 'getUpdates' failed!`가 포함되면 OpenClaw는 이제 이를 복구 가능한 네트워크 오류로 재시도합니다. - - 폴링 시작 중 OpenClaw는 grammY에 성공한 시작 `getMe` 프로브를 재사용하므로, 러너가 첫 `getUpdates` 전에 두 번째 `getMe`를 수행할 필요가 없습니다. - - 폴링 시작 중 일시적인 네트워크 오류로 `deleteWebhook`이 실패하면 OpenClaw는 다른 사전 폴링 제어 평면 호출을 만들지 않고 롱 폴링으로 계속 진행합니다. 아직 활성 상태인 webhook은 `getUpdates` 충돌로 나타납니다. 그러면 OpenClaw는 Telegram 전송을 다시 빌드하고 webhook 정리를 재시도합니다. - - Telegram 소켓이 짧은 고정 주기로 재활용된다면 낮은 `channels.telegram.timeoutSeconds` 값을 확인하세요. 봇 클라이언트는 구성된 값이 송신 및 `getUpdates` 요청 가드보다 낮으면 이를 제한하지만, 이전 릴리스에서는 이 값이 해당 가드보다 낮게 설정된 경우 모든 폴링이나 응답이 중단될 수 있었습니다. - - 로그에 `Polling stall detected`가 포함되면 OpenClaw는 기본적으로 완료된 롱 폴 활성 상태 없이 120초가 지난 뒤 폴링을 다시 시작하고 Telegram 전송을 다시 빌드합니다. - - `openclaw channels status --probe` 및 `openclaw doctor`는 실행 중인 폴링 계정이 시작 유예 후 `getUpdates`를 완료하지 않았거나, 실행 중인 webhook 계정이 시작 유예 후 `setWebhook`을 완료하지 않았거나, 마지막으로 성공한 폴링 전송 활동이 오래된 경우 경고합니다. - - 장시간 실행되는 `getUpdates` 호출은 정상인데도 호스트가 잘못된 폴링 중단 재시작을 계속 보고하는 경우에만 `channels.telegram.pollingStallThresholdMs`를 늘리세요. 지속적인 중단은 일반적으로 호스트와 `api.telegram.org` 사이의 proxy, DNS, IPv6 또는 TLS 송신 문제를 가리킵니다. - - Telegram은 Bot API 전송에 대해 `HTTP_PROXY`, `HTTPS_PROXY`, `ALL_PROXY` 및 이들의 소문자 변형을 포함한 프로세스 proxy 환경 변수도 따릅니다. `NO_PROXY` / `no_proxy`는 여전히 `api.telegram.org`를 우회할 수 있습니다. - - 서비스 환경에서 OpenClaw 관리 proxy가 `OPENCLAW_PROXY_URL`을 통해 구성되어 있고 표준 proxy 환경 변수가 없으면, Telegram도 Bot API 전송에 해당 URL을 사용합니다. - - 직접 송신/TLS가 불안정한 VPS 호스트에서는 `channels.telegram.proxy`를 통해 Telegram API 호출을 라우팅하세요. + - Node 22+와 사용자 지정 fetch/proxy 조합은 AbortSignal 유형이 일치하지 않으면 즉시 중단 동작을 유발할 수 있습니다. + - 일부 호스트는 `api.telegram.org`를 IPv6로 먼저 해석합니다. 손상된 IPv6 송신은 간헐적인 Telegram API 실패를 일으킬 수 있습니다. + - 로그에 `TypeError: fetch failed` 또는 `Network request for 'getUpdates' failed!`가 포함되어 있으면 OpenClaw는 이제 이를 복구 가능한 네트워크 오류로 재시도합니다. + - 폴링 시작 중 OpenClaw는 성공한 시작 `getMe` 프로브를 grammY에 재사용하므로 러너가 첫 `getUpdates` 전에 두 번째 `getMe`를 수행할 필요가 없습니다. + - 폴링 시작 중 일시적인 네트워크 오류로 `deleteWebhook`이 실패하면 OpenClaw는 또 다른 사전 폴링 제어 플레인 호출을 수행하는 대신 롱 폴링으로 계속 진행합니다. 아직 활성 상태인 Webhook은 `getUpdates` 충돌로 나타납니다. 그러면 OpenClaw는 Telegram 전송을 다시 빌드하고 Webhook 정리를 재시도합니다. + - Telegram 소켓이 짧은 고정 주기로 재활용된다면 낮은 `channels.telegram.timeoutSeconds` 값을 확인하세요. 봇 클라이언트는 구성된 값을 송신 및 `getUpdates` 요청 가드 아래로 내려가지 않게 제한하지만, 이전 릴리스에서는 이 값이 해당 가드보다 낮게 설정되었을 때 모든 폴링 또는 응답이 중단될 수 있었습니다. + - 로그에 `Polling stall detected`가 포함되어 있으면 OpenClaw는 기본적으로 완료된 롱 폴링 활성 상태가 120초 동안 없을 때 폴링을 다시 시작하고 Telegram 전송을 다시 빌드합니다. + - `openclaw channels status --probe` 및 `openclaw doctor`는 실행 중인 폴링 계정이 시작 유예 시간 후 `getUpdates`를 완료하지 못했거나, 실행 중인 Webhook 계정이 시작 유예 시간 후 `setWebhook`을 완료하지 못했거나, 마지막으로 성공한 폴링 전송 활동이 오래된 경우 경고합니다. + - 장시간 실행되는 `getUpdates` 호출이 정상인데도 호스트가 잘못된 폴링 중단 재시작을 계속 보고할 때만 `channels.telegram.pollingStallThresholdMs`를 늘리세요. 지속적인 중단은 보통 호스트와 `api.telegram.org` 사이의 프록시, DNS, IPv6 또는 TLS 송신 문제를 가리킵니다. + - Telegram은 Bot API 전송에 대해 `HTTP_PROXY`, `HTTPS_PROXY`, `ALL_PROXY` 및 각각의 소문자 변형을 포함한 프로세스 프록시 환경 변수도 따릅니다. `NO_PROXY` / `no_proxy`는 여전히 `api.telegram.org`를 우회할 수 있습니다. + - 서비스 환경에서 OpenClaw 관리 프록시가 `OPENCLAW_PROXY_URL`을 통해 구성되어 있고 표준 프록시 환경 변수가 없으면 Telegram도 Bot API 전송에 해당 URL을 사용합니다. + - 직접 송신/TLS가 불안정한 VPS 호스트에서는 Telegram API 호출을 `channels.telegram.proxy`를 통해 라우팅하세요. ```yaml channels: @@ -891,8 +929,8 @@ channels: proxy: socks5://:@proxy-host:1080 ``` - - Node 22+는 기본적으로 `autoSelectFamily=true`입니다(WSL2 제외). Telegram DNS 결과 순서는 `OPENCLAW_TELEGRAM_DNS_RESULT_ORDER`, 그다음 `channels.telegram.network.dnsResultOrder`, 그다음 `NODE_OPTIONS=--dns-result-order=ipv4first` 같은 프로세스 기본값을 따릅니다. 아무것도 적용되지 않으면 Node 22+는 `ipv4first`로 폴백합니다. - - 호스트가 WSL2이거나 IPv4 전용 동작에서 명시적으로 더 잘 작동한다면 패밀리 선택을 강제하세요. + - Node 22+는 기본적으로 `autoSelectFamily=true`입니다(WSL2 제외). Telegram DNS 결과 순서는 `OPENCLAW_TELEGRAM_DNS_RESULT_ORDER`, `channels.telegram.network.dnsResultOrder`, `NODE_OPTIONS=--dns-result-order=ipv4first` 같은 프로세스 기본값 순서로 따릅니다. 아무것도 적용되지 않으면 Node 22+는 `ipv4first`로 대체됩니다. + - 호스트가 WSL2이거나 IPv4 전용 동작이 명시적으로 더 잘 작동한다면 패밀리 선택을 강제하세요. ```yaml channels: @@ -901,7 +939,7 @@ channels: autoSelectFamily: false ``` - - RFC 2544 벤치마크 범위 응답(`198.18.0.0/15`)은 기본적으로 Telegram 미디어 다운로드에 이미 허용됩니다. 신뢰할 수 있는 가짜 IP 또는 투명 proxy가 미디어 다운로드 중 `api.telegram.org`를 다른 private/internal/special-use 주소로 다시 쓰는 경우, Telegram 전용 우회를 선택할 수 있습니다. + - RFC 2544 벤치마크 범위 응답(`198.18.0.0/15`)은 기본적으로 Telegram 미디어 다운로드에 이미 허용됩니다. 신뢰할 수 있는 fake-IP 또는 투명 프록시가 미디어 다운로드 중 `api.telegram.org`를 다른 private/internal/special-use 주소로 재작성한다면 Telegram 전용 우회에 옵트인할 수 있습니다. ```yaml channels: @@ -910,15 +948,15 @@ channels: dangerouslyAllowPrivateNetwork: true ``` - - 동일한 옵트인은 계정별로 - `channels.telegram.accounts..network.dangerouslyAllowPrivateNetwork`에서도 사용할 수 있습니다. - - proxy가 Telegram 미디어 호스트를 `198.18.x.x`로 확인한다면, 먼저 위험한 플래그를 꺼 둔 상태로 두세요. Telegram 미디어는 기본적으로 RFC 2544 벤치마크 범위를 이미 허용합니다. + - 계정별로도 동일한 옵트인이 + `channels.telegram.accounts..network.dangerouslyAllowPrivateNetwork`에서 제공됩니다. + - 프록시가 Telegram 미디어 호스트를 `198.18.x.x`로 해석한다면 먼저 위험한 플래그를 끈 상태로 두세요. Telegram 미디어는 기본적으로 RFC 2544 벤치마크 범위를 이미 허용합니다. `channels.telegram.network.dangerouslyAllowPrivateNetwork`는 Telegram - 미디어 SSRF 보호를 약화합니다. Clash, Mihomo 또는 Surge 가짜 IP 라우팅처럼 신뢰할 수 있는 운영자 제어 proxy - 환경에서 RFC 2544 벤치마크 범위 밖의 private 또는 special-use 응답을 합성하는 경우에만 사용하세요. - 일반 공용 인터넷 Telegram 액세스에는 꺼 두세요. + 미디어 SSRF 보호를 약화합니다. Clash, Mihomo, Surge fake-IP 라우팅처럼 + RFC 2544 벤치마크 범위 밖의 private 또는 special-use 응답을 합성하는 + 신뢰할 수 있는 운영자 제어 프록시 환경에서만 사용하세요. 일반적인 공개 인터넷 Telegram 접근에는 꺼 두세요. - 환경 재정의(임시): @@ -941,18 +979,18 @@ dig +short api.telegram.org AAAA 기본 참조: [구성 참조 - Telegram](/ko/gateway/config-channels#telegram). - + -- 시작/인증: `enabled`, `botToken`, `tokenFile`, `accounts.*` (`tokenFile`은 일반 파일을 가리켜야 하며, 심볼릭 링크는 거부됩니다) -- 액세스 제어: `dmPolicy`, `allowFrom`, `groupPolicy`, `groupAllowFrom`, `groups`, `groups.*.topics.*`, 최상위 `bindings[]` (`type: "acp"`) -- exec 승인: `execApprovals`, `accounts.*.execApprovals` +- 시작/인증: `enabled`, `botToken`, `tokenFile`, `accounts.*`(`tokenFile`은 일반 파일을 가리켜야 하며 심볼릭 링크는 거부됨) +- 접근 제어: `dmPolicy`, `allowFrom`, `groupPolicy`, `groupAllowFrom`, `groups`, `groups.*.topics.*`, 최상위 `bindings[]`(`type: "acp"`) +- 실행 승인: `execApprovals`, `accounts.*.execApprovals` - 명령/메뉴: `commands.native`, `commands.nativeSkills`, `customCommands` - 스레딩/응답: `replyToMode`, `dm.threadReplies`, `direct.*.threadReplies` - 스트리밍: `streaming`(미리 보기), `streaming.preview.toolProgress`, `blockStreaming` - 형식/전달: `textChunkLimit`, `chunkMode`, `linkPreview`, `responsePrefix` - 미디어/네트워크: `mediaMaxMb`, `mediaGroupFlushMs`, `timeoutSeconds`, `pollingStallThresholdMs`, `retry`, `network.autoSelectFamily`, `network.dangerouslyAllowPrivateNetwork`, `proxy` -- 사용자 지정 API 루트: `apiRoot`(Bot API 루트만 해당, `/bot`을 포함하지 마세요) -- webhook: `webhookUrl`, `webhookSecret`, `webhookPath`, `webhookHost` +- 사용자 지정 API 루트: `apiRoot`(Bot API 루트만 해당, `/bot` 포함 금지) +- Webhook: `webhookUrl`, `webhookSecret`, `webhookPath`, `webhookHost` - 작업/기능: `capabilities.inlineButtons`, `actions.sendMessage|editMessage|deleteMessage|reactions|sticker` - 반응: `reactionNotifications`, `reactionLevel` - 오류: `errorPolicy`, `errorCooldownMs` @@ -961,7 +999,7 @@ dig +short api.telegram.org AAAA -다중 계정 우선순위: 두 개 이상의 계정 ID가 구성된 경우 기본 라우팅을 명시하려면 `channels.telegram.defaultAccount`를 설정하세요(또는 `channels.telegram.accounts.default`를 포함하세요). 그렇지 않으면 OpenClaw는 첫 번째 정규화된 계정 ID로 폴백하고 `openclaw doctor`가 경고합니다. 이름 있는 계정은 `channels.telegram.allowFrom` / `groupAllowFrom`을 상속하지만 `accounts.default.*` 값은 상속하지 않습니다. +다중 계정 우선순위: 두 개 이상의 계정 ID가 구성된 경우 기본 라우팅을 명시하려면 `channels.telegram.defaultAccount`를 설정하세요(또는 `channels.telegram.accounts.default` 포함). 그렇지 않으면 OpenClaw는 정규화된 첫 번째 계정 ID로 대체하고 `openclaw doctor`가 경고합니다. 이름 있는 계정은 `channels.telegram.allowFrom` / `groupAllowFrom`을 상속하지만 `accounts.default.*` 값은 상속하지 않습니다. ## 관련 항목 @@ -971,18 +1009,18 @@ dig +short api.telegram.org AAAA Telegram 사용자를 Gateway에 페어링합니다. - 그룹 및 토픽 허용 목록 동작. + 그룹 및 주제 허용 목록 동작입니다. 인바운드 메시지를 에이전트로 라우팅합니다. - 위협 모델과 강화. + 위협 모델 및 강화입니다. - 그룹과 토픽을 에이전트에 매핑합니다. + 그룹과 주제를 에이전트에 매핑합니다. - 교차 채널 진단. + 채널 간 진단입니다. diff --git a/docs/ko/concepts/streaming.md b/docs/ko/concepts/streaming.md index d71e29b31..3a84f10b6 100644 --- a/docs/ko/concepts/streaming.md +++ b/docs/ko/concepts/streaming.md @@ -1,15 +1,15 @@ --- read_when: - - 채널에서 스트리밍 또는 청크 처리 방식 설명 - - 블록 스트리밍 또는 채널 청크 처리 동작 변경 + - 채널에서 스트리밍 또는 청크 처리가 작동하는 방식 설명 + - 블록 스트리밍 또는 채널 청킹 동작 변경 - 중복/조기 블록 응답 또는 채널 미리보기 스트리밍 디버깅 -summary: 스트리밍 + 청킹 동작 (블록 응답, 채널 미리보기 스트리밍, 모드 매핑) +summary: 스트리밍 + 청크 처리 동작(블록 답장, 채널 미리보기 스트리밍, 모드 매핑) title: 스트리밍 및 청크 처리 x-i18n: - generated_at: "2026-05-04T06:23:53Z" + generated_at: "2026-05-04T07:03:02Z" model: gpt-5.5 provider: openai - source_hash: fcb41ceb5602ab42c3fd41a59de62cc965ea61fdbc058c052fb93689a9c5299b + source_hash: ff7b6cd8127255352fe16fb746469e9828e7d5aea183d3799ab10cc768515bd1 source_path: concepts/streaming.md workflow: 16 --- @@ -19,11 +19,11 @@ OpenClaw에는 두 개의 별도 스트리밍 계층이 있습니다. - **블록 스트리밍(채널):** 어시스턴트가 작성하는 동안 완료된 **블록**을 내보냅니다. 이는 일반 채널 메시지입니다(토큰 델타가 아님). - **미리보기 스트리밍(Telegram/Discord/Slack):** 생성 중 임시 **미리보기 메시지**를 업데이트합니다. -현재 채널 메시지로 보내는 **진정한 토큰 델타 스트리밍**은 없습니다. 미리보기 스트리밍은 메시지 기반입니다(전송 + 편집/추가). +현재 채널 메시지에는 **진정한 토큰 델타 스트리밍**이 없습니다. 미리보기 스트리밍은 메시지 기반입니다(전송 + 수정/추가). ## 블록 스트리밍(채널 메시지) -블록 스트리밍은 사용할 수 있게 되는 대로 어시스턴트 출력을 큼직한 청크로 보냅니다. +블록 스트리밍은 어시스턴트 출력을 사용할 수 있게 되는 대로 큰 단위의 청크로 보냅니다. ``` Model output @@ -37,107 +37,124 @@ Model output 범례: -- `text_delta/events`: 모델 스트림 이벤트입니다(비스트리밍 모델에서는 드물 수 있음). -- `chunker`: 최소/최대 경계와 분할 선호도를 적용하는 `EmbeddedBlockChunker`입니다. -- `channel send`: 실제 발신 메시지입니다(블록 응답). +- `text_delta/events`: 모델 스트림 이벤트(비스트리밍 모델에서는 드물 수 있음). +- `chunker`: 최소/최대 범위 + 중단 선호도를 적용하는 `EmbeddedBlockChunker`. +- `channel send`: 실제 발신 메시지(블록 답장). -**제어:** +**제어 항목:** - `agents.defaults.blockStreamingDefault`: `"on"`/`"off"`(기본값 꺼짐). - 채널 재정의: 채널별로 `"on"`/`"off"`를 강제하는 `*.blockStreaming`(및 계정별 변형). - `agents.defaults.blockStreamingBreak`: `"text_end"` 또는 `"message_end"`. - `agents.defaults.blockStreamingChunk`: `{ minChars, maxChars, breakPreference? }`. - `agents.defaults.blockStreamingCoalesce`: `{ minChars?, maxChars?, idleMs? }`(전송 전 스트리밍된 블록 병합). -- 채널 하드 한도: `*.textChunkLimit`(예: `channels.whatsapp.textChunkLimit`). -- 채널 청크 모드: `*.chunkMode`(`length`가 기본값, `newline`은 길이 기준 청킹 전에 빈 줄(문단 경계)에서 분할). -- Discord 소프트 한도: `channels.discord.maxLinesPerMessage`(기본값 17)는 UI 잘림을 피하기 위해 긴 응답을 분할합니다. +- 채널 하드 제한: `*.textChunkLimit`(예: `channels.whatsapp.textChunkLimit`). +- 채널 청크 모드: `*.chunkMode`(`length`가 기본값, `newline`은 길이 청킹 전에 빈 줄(문단 경계)에서 분할). +- Discord 소프트 제한: UI 잘림을 피하기 위해 긴 답장을 분할하는 `channels.discord.maxLinesPerMessage`(기본값 17). **경계 의미:** -- `text_end`: 청크 처리기가 내보내는 즉시 블록을 스트리밍하고, 각 `text_end`에서 플러시합니다. +- `text_end`: chunker가 내보내는 즉시 블록을 스트리밍하고, 각 `text_end`에서 플러시합니다. - `message_end`: 어시스턴트 메시지가 끝날 때까지 기다린 다음 버퍼링된 출력을 플러시합니다. -`message_end`는 버퍼링된 텍스트가 `maxChars`를 초과하면 여전히 청크 처리기를 사용하므로, 끝에서 여러 청크를 내보낼 수 있습니다. +버퍼링된 텍스트가 `maxChars`를 초과하면 `message_end`도 여전히 chunker를 사용하므로, 마지막에 여러 청크를 내보낼 수 있습니다. -### 블록 스트리밍의 미디어 전달 +### 블록 스트리밍에서의 미디어 전달 -`MEDIA:` 지시문은 일반 전달 메타데이터입니다. 블록 스트리밍이 미디어 블록을 일찍 보내면 OpenClaw는 해당 턴의 전달을 기억합니다. 최종 어시스턴트 페이로드가 같은 미디어 URL을 반복하면, 최종 전달은 첨부 파일을 다시 보내는 대신 중복 미디어를 제거합니다. +`MEDIA:` 지시문은 일반 전달 메타데이터입니다. 블록 스트리밍이 미디어 블록을 +일찍 보내면 OpenClaw는 해당 턴의 전달을 기억합니다. 최종 +어시스턴트 페이로드가 같은 미디어 URL을 반복하면 최종 전달은 +첨부 파일을 다시 보내는 대신 중복 미디어를 제거합니다. -완전히 중복되는 최종 페이로드는 억제됩니다. 최종 페이로드가 이미 스트리밍된 미디어 주변에 별도의 텍스트를 추가하면, OpenClaw는 미디어를 한 번만 전달하도록 유지하면서 새 텍스트는 계속 보냅니다. 이렇게 하면 에이전트가 스트리밍 중 `MEDIA:`를 내보내고 제공자도 완료된 응답에 이를 포함할 때 Telegram 같은 채널에서 음성 메모나 파일이 중복되는 일을 방지합니다. +정확히 중복되는 최종 페이로드는 억제됩니다. 최종 페이로드가 이미 +스트리밍된 미디어 주변에 별도의 텍스트를 추가하면, OpenClaw는 미디어는 +한 번만 전달하도록 유지하면서 새 텍스트는 계속 보냅니다. 이렇게 하면 에이전트가 +스트리밍 중 `MEDIA:`를 내보내고 제공자도 완료된 답장에 이를 포함할 때, +Telegram 같은 채널에서 음성 메모나 파일이 중복되는 것을 방지합니다. -## 청킹 알고리즘(하한/상한 경계) +## 청킹 알고리즘(하한/상한) 블록 청킹은 `EmbeddedBlockChunker`로 구현됩니다. -- **하한 경계:** 버퍼 >= `minChars`가 될 때까지 내보내지 않습니다(강제된 경우 제외). -- **상한 경계:** `maxChars` 전에 분할하는 것을 선호하며, 강제된 경우 `maxChars`에서 분할합니다. -- **분할 선호도:** `paragraph` → `newline` → `sentence` → `whitespace` → 하드 분할. -- **코드 펜스:** 펜스 내부에서는 절대 분할하지 않습니다. `maxChars`에서 강제 분할할 때는 Markdown이 유효하게 유지되도록 펜스를 닫고 다시 엽니다. +- **하한:** 버퍼 >= `minChars`가 될 때까지 내보내지 않습니다(강제되는 경우 제외). +- **상한:** `maxChars` 전에 분할하는 것을 선호합니다. 강제되면 `maxChars`에서 분할합니다. +- **중단 선호도:** `paragraph` → `newline` → `sentence` → `whitespace` → 강제 중단. +- **코드 펜스:** 펜스 내부에서는 절대 분할하지 않습니다. `maxChars`에서 강제되는 경우 Markdown이 유효하도록 펜스를 닫고 다시 엽니다. -`maxChars`는 채널 `textChunkLimit`로 제한되므로 채널별 한도를 초과할 수 없습니다. +`maxChars`는 채널 `textChunkLimit`로 제한되므로, 채널별 제한을 초과할 수 없습니다. ## 병합(스트리밍된 블록 병합) -블록 스트리밍이 활성화되면 OpenClaw는 전송하기 전에 **연속된 블록 청크를 병합**할 수 있습니다. 이렇게 하면 점진적 출력을 제공하면서도 “한 줄 스팸”을 줄일 수 있습니다. +블록 스트리밍이 활성화되면 OpenClaw는 발신 전에 **연속된 블록 청크를 병합**할 수 있습니다. 이렇게 하면 점진적인 출력은 제공하면서도 +“한 줄 스팸”을 줄일 수 있습니다. -- 병합은 플러시하기 전에 **유휴 간격**(`idleMs`)을 기다립니다. -- 버퍼는 `maxChars`로 제한되며, 이를 초과하면 플러시됩니다. -- `minChars`는 충분한 텍스트가 누적될 때까지 작은 조각이 전송되지 않도록 합니다(최종 플러시는 항상 남은 텍스트를 보냄). -- 조인자는 `blockStreamingChunk.breakPreference`에서 파생됩니다(`paragraph` → `\n\n`, `newline` → `\n`, `sentence` → 공백). -- 채널 재정의는 `*.blockStreamingCoalesce`를 통해 사용할 수 있습니다(계정별 설정 포함). +- 병합은 플러시 전에 **유휴 간격**(`idleMs`)을 기다립니다. +- 버퍼는 `maxChars`로 제한되며 이를 초과하면 플러시됩니다. +- `minChars`는 충분한 텍스트가 누적될 때까지 작은 조각이 전송되는 것을 방지합니다 + (최종 플러시는 항상 남은 텍스트를 보냄). +- 조이너는 `blockStreamingChunk.breakPreference`에서 파생됩니다 + (`paragraph` → `\n\n`, `newline` → `\n`, `sentence` → 공백). +- 채널 재정의는 `*.blockStreamingCoalesce`를 통해 사용할 수 있습니다(계정별 구성 포함). - 기본 병합 `minChars`는 재정의하지 않는 한 Signal/Slack/Discord에서 1500으로 올라갑니다. ## 블록 사이의 사람 같은 속도 조절 -블록 스트리밍이 활성화되면 블록 응답 사이에(첫 번째 블록 이후) **무작위 일시 중지**를 추가할 수 있습니다. 이렇게 하면 여러 말풍선 응답이 더 자연스럽게 느껴집니다. +블록 스트리밍이 활성화되면 블록 답장 사이(첫 번째 블록 이후)에 **무작위 일시정지**를 추가할 수 있습니다. 이렇게 하면 여러 말풍선 응답이 +더 자연스럽게 느껴집니다. -- 설정: `agents.defaults.humanDelay`(`agents.list[].humanDelay`로 에이전트별 재정의). +- 구성: `agents.defaults.humanDelay`(`agents.list[].humanDelay`로 에이전트별 재정의). - 모드: `off`(기본값), `natural`(800~2500ms), `custom`(`minMs`/`maxMs`). -- **블록 응답**에만 적용되며, 최종 응답이나 도구 요약에는 적용되지 않습니다. +- **블록 답장**에만 적용되며, 최종 답장이나 도구 요약에는 적용되지 않습니다. ## "청크 스트리밍 또는 전체 스트리밍" 이는 다음에 매핑됩니다. -- **청크 스트리밍:** `blockStreamingDefault: "on"` + `blockStreamingBreak: "text_end"`(진행하면서 내보냄). Telegram이 아닌 채널은 `*.blockStreaming: true`도 필요합니다. -- **끝에서 전체 스트리밍:** `blockStreamingBreak: "message_end"`(한 번 플러시, 매우 길면 여러 청크 가능). -- **블록 스트리밍 없음:** `blockStreamingDefault: "off"`(최종 응답만). +- **청크 스트리밍:** `blockStreamingDefault: "on"` + `blockStreamingBreak: "text_end"`(진행 중 내보내기). Telegram이 아닌 채널에는 `*.blockStreaming: true`도 필요합니다. +- **마지막에 전체 스트리밍:** `blockStreamingBreak: "message_end"`(한 번 플러시, 매우 길면 여러 청크일 수 있음). +- **블록 스트리밍 없음:** `blockStreamingDefault: "off"`(최종 답장만). -**채널 참고:** `*.blockStreaming`이 명시적으로 `true`로 설정되지 않는 한 블록 스트리밍은 **꺼져 있습니다**. 채널은 블록 응답 없이 실시간 미리보기(`channels..streaming`)를 스트리밍할 수 있습니다. +**채널 참고:** `*.blockStreaming`이 명시적으로 `true`로 설정되지 않는 한 블록 스트리밍은 **꺼져 있습니다**. 채널은 블록 답장 없이 실시간 미리보기 +(`channels..streaming`)를 스트리밍할 수 있습니다. -설정 위치 알림: `blockStreaming*` 기본값은 루트 설정이 아니라 `agents.defaults` 아래에 있습니다. +구성 위치 알림: `blockStreaming*` 기본값은 루트 구성이 아니라 +`agents.defaults` 아래에 있습니다. ## 미리보기 스트리밍 모드 -정식 키: `channels..streaming` +표준 키: `channels..streaming` 모드: - `off`: 미리보기 스트리밍을 비활성화합니다. -- `partial`: 최신 텍스트로 교체되는 단일 미리보기입니다. +- `partial`: 최신 텍스트로 대체되는 단일 미리보기. - `block`: 청크/추가 단계로 미리보기를 업데이트합니다. -- `progress`: 생성 중 진행/상태 미리보기, 완료 시 최종 답변입니다. +- `progress`: 생성 중 진행률/상태 미리보기, 완료 시 최종 답변. -`streaming.mode: "block"`은 Discord와 Telegram처럼 편집 가능한 채널을 위한 미리보기 스트리밍 모드입니다. 이것이 해당 채널에서 채널 블록 전달을 활성화하지는 않습니다. 일반 블록 응답을 원할 때는 `streaming.block.enabled` 또는 레거시 `blockStreaming` 채널 키를 사용하세요. Microsoft Teams는 예외입니다. 초안 미리보기 블록 전송이 없으므로 `streaming.mode: "block"`은 네이티브 partial/progress 스트리밍 대신 Teams 블록 전달에 매핑됩니다. +`streaming.mode: "block"`은 Discord와 Telegram처럼 수정 가능한 채널을 위한 +미리보기 스트리밍 모드입니다. 이 설정은 해당 채널에서 채널 블록 전달을 활성화하지 않습니다. +일반 블록 답장을 원하면 `streaming.block.enabled` 또는 레거시 +`blockStreaming` 채널 키를 사용하세요. Microsoft Teams는 예외입니다. 초안 미리보기 +블록 전송이 없으므로 `streaming.mode: "block"`은 네이티브 partial/progress 스트리밍 대신 Teams 블록 전달로 매핑됩니다. ### 채널 매핑 -| 채널 | `off` | `partial` | `block` | `progress` | -| ---------- | ----- | --------- | ------- | ------------------- | -| Telegram | ✅ | ✅ | ✅ | 편집 가능한 진행 초안 | -| Discord | ✅ | ✅ | ✅ | 편집 가능한 진행 초안 | -| Slack | ✅ | ✅ | ✅ | ✅ | -| Mattermost | ✅ | ✅ | ✅ | ✅ | -| MS Teams | ✅ | ✅ | ✅ | 네이티브 진행 스트림 | +| 채널 | `off` | `partial` | `block` | `progress` | +| ---------- | ----- | --------- | ------- | ----------------------- | +| Telegram | ✅ | ✅ | ✅ | 수정 가능한 진행 초안 | +| Discord | ✅ | ✅ | ✅ | 수정 가능한 진행 초안 | +| Slack | ✅ | ✅ | ✅ | ✅ | +| Mattermost | ✅ | ✅ | ✅ | ✅ | +| MS Teams | ✅ | ✅ | ✅ | 네이티브 진행 스트림 | Slack 전용: - `channels.slack.streaming.nativeTransport`는 `channels.slack.streaming.mode="partial"`일 때 Slack 네이티브 스트리밍 API 호출을 전환합니다(기본값: `true`). -- Slack 네이티브 스트리밍과 Slack 어시스턴트 스레드 상태에는 응답 스레드 대상이 필요합니다. 최상위 DM은 해당 스레드 스타일 미리보기를 표시하지 않지만, Slack 초안 미리보기 게시물과 편집은 계속 사용할 수 있습니다. +- Slack 네이티브 스트리밍과 Slack 어시스턴트 스레드 상태에는 답장 스레드 대상이 필요합니다. 최상위 DM은 해당 스레드 스타일 미리보기를 표시하지 않지만, Slack 초안 미리보기 게시물과 수정을 계속 사용할 수 있습니다. 레거시 키 마이그레이션: -- Telegram: 레거시 `streamMode` 및 스칼라/불리언 `streaming` 값은 doctor/config 호환성 경로에서 감지되어 `streaming.mode`로 마이그레이션됩니다. +- Telegram: 레거시 `streamMode`와 스칼라/불리언 `streaming` 값은 doctor/config 호환성 경로에서 감지되어 `streaming.mode`로 마이그레이션됩니다. - Discord: `streamMode` + 불리언 `streaming`은 `streaming` 열거형으로 자동 마이그레이션됩니다. - Slack: `streamMode`는 `streaming.mode`로 자동 마이그레이션됩니다. 불리언 `streaming`은 `streaming.mode`와 `streaming.nativeTransport`로 자동 마이그레이션됩니다. 레거시 `nativeStreaming`은 `streaming.nativeTransport`로 자동 마이그레이션됩니다. @@ -145,52 +162,52 @@ Slack 전용: Telegram: -- DM과 그룹/토픽 전반에서 `sendMessage` + `editMessageText` 미리보기 업데이트를 사용합니다. -- 미리보기가 약 1분 동안 표시된 경우, 제자리에서 편집하는 대신 새로운 최종 메시지를 보낸 다음 Telegram의 타임스탬프가 응답 완료를 반영하도록 미리보기를 정리합니다. -- Telegram 블록 스트리밍이 명시적으로 활성화된 경우 미리보기 스트리밍은 건너뜁니다(이중 스트리밍 방지). -- `/reasoning stream`은 최종 전달 후 삭제되는 임시 미리보기에 추론을 쓸 수 있습니다. +- DM 및 그룹/토픽 전반에서 `sendMessage` + `editMessageText` 미리보기 업데이트를 사용합니다. +- 미리보기가 약 1분 동안 표시된 경우 제자리에서 수정하는 대신 새 최종 메시지를 보낸 다음, Telegram의 타임스탬프가 답장 완료를 반영하도록 미리보기를 정리합니다. +- Telegram 블록 스트리밍이 명시적으로 활성화된 경우 미리보기 스트리밍을 건너뜁니다(이중 스트리밍 방지). +- `/reasoning stream`은 최종 전달 후 삭제되는 일시적 미리보기에 추론을 쓸 수 있습니다. Discord: -- 전송 + 편집 미리보기 메시지를 사용합니다. +- 전송 + 수정 미리보기 메시지를 사용합니다. - `block` 모드는 초안 청킹(`draftChunk`)을 사용합니다. -- Discord 블록 스트리밍이 명시적으로 활성화된 경우 미리보기 스트리밍은 건너뜁니다. -- 최종 미디어, 오류, 명시적 응답 페이로드는 새 초안을 플러시하지 않고 대기 중인 미리보기를 취소한 다음 일반 전달을 사용합니다. +- Discord 블록 스트리밍이 명시적으로 활성화된 경우 미리보기 스트리밍을 건너뜁니다. +- 최종 미디어, 오류 및 명시적 답장 페이로드는 새 초안을 플러시하지 않고 보류 중인 미리보기를 취소한 다음 일반 전달을 사용합니다. Slack: -- `partial`은 사용할 수 있을 때 Slack 네이티브 스트리밍(`chat.startStream`/`append`/`stop`)을 사용할 수 있습니다. +- `partial`은 사용 가능한 경우 Slack 네이티브 스트리밍(`chat.startStream`/`append`/`stop`)을 사용할 수 있습니다. - `block`은 추가 스타일 초안 미리보기를 사용합니다. - `progress`는 상태 미리보기 텍스트를 사용한 다음 최종 답변을 보냅니다. -- 응답 스레드가 없는 최상위 DM은 Slack 네이티브 스트리밍 대신 초안 미리보기 게시물과 편집을 사용합니다. -- 네이티브 및 초안 미리보기 스트리밍은 해당 턴의 블록 응답을 억제하므로 Slack 응답은 하나의 전달 경로로만 스트리밍됩니다. -- 최종 미디어/오류 페이로드와 진행 최종 메시지는 일회용 초안 메시지를 만들지 않습니다. 미리보기를 편집할 수 있는 텍스트/블록 최종 메시지만 대기 중인 초안 텍스트를 플러시합니다. +- 답장 스레드가 없는 최상위 DM은 Slack 네이티브 스트리밍 대신 초안 미리보기 게시물과 수정을 사용합니다. +- 네이티브 및 초안 미리보기 스트리밍은 해당 턴의 블록 답장을 억제하므로 Slack 답장은 하나의 전달 경로로만 스트리밍됩니다. +- 최종 미디어/오류 페이로드와 진행 최종 메시지는 일회용 초안 메시지를 만들지 않습니다. 미리보기를 수정할 수 있는 텍스트/블록 최종 메시지만 보류 중인 초안 텍스트를 플러시합니다. Mattermost: -- 사고 과정, 도구 활동, 부분 응답 텍스트를 단일 초안 미리보기 게시물로 스트리밍하며, 최종 답변을 보내도 안전할 때 제자리에서 최종화합니다. -- 미리보기 게시물이 삭제되었거나 최종화 시 사용할 수 없는 경우 새 최종 게시물을 보내는 방식으로 폴백합니다. -- 최종 미디어/오류 페이로드는 임시 미리보기 게시물을 플러시하는 대신 일반 전달 전에 대기 중인 미리보기 업데이트를 취소합니다. +- 생각, 도구 활동, 부분 답장 텍스트를 단일 초안 미리보기 게시물로 스트리밍하며, 최종 답변을 안전하게 보낼 수 있을 때 제자리에서 완료합니다. +- 완료 시점에 미리보기 게시물이 삭제되었거나 사용할 수 없는 경우 새 최종 게시물을 보내는 방식으로 폴백합니다. +- 최종 미디어/오류 페이로드는 임시 미리보기 게시물을 플러시하는 대신 일반 전달 전에 보류 중인 미리보기 업데이트를 취소합니다. Matrix: -- 최종 텍스트가 미리보기 이벤트를 재사용할 수 있으면 초안 미리보기를 제자리에서 최종화합니다. -- 미디어 전용, 오류, 응답 대상 불일치 최종 메시지는 일반 전달 전에 대기 중인 미리보기 업데이트를 취소합니다. 이미 표시된 오래된 미리보기는 삭제됩니다. +- 최종 텍스트가 미리보기 이벤트를 재사용할 수 있으면 초안 미리보기가 제자리에서 완료됩니다. +- 미디어 전용, 오류, 답장 대상 불일치 최종 메시지는 일반 전달 전에 보류 중인 미리보기 업데이트를 취소합니다. 이미 표시된 오래된 미리보기는 삭제 처리됩니다. -### 도구 진행 미리보기 업데이트 +### 도구 진행률 미리보기 업데이트 -미리보기 스트리밍에는 **도구 진행** 업데이트도 포함될 수 있습니다. 이는 "웹 검색 중", "파일 읽는 중", "도구 호출 중" 같은 짧은 상태 줄로, 도구가 실행되는 동안 최종 응답보다 먼저 같은 미리보기 메시지에 표시됩니다. 이렇게 하면 여러 단계의 도구 턴이 첫 사고 과정 미리보기와 최종 답변 사이에 조용히 멈춰 있는 대신 시각적으로 살아 있게 유지됩니다. +미리보기 스트리밍에는 도구가 실행되는 동안 최종 답장보다 앞서 같은 미리보기 메시지에 표시되는 "웹 검색 중", "파일 읽는 중", "도구 호출 중" 같은 짧은 상태 줄인 **도구 진행률** 업데이트도 포함될 수 있습니다. 이렇게 하면 여러 단계의 도구 턴이 첫 생각 미리보기와 최종 답변 사이에서 조용히 멈춰 있는 대신 시각적으로 계속 살아 있게 됩니다. 지원되는 표면: -- **Discord**, **Slack**, **Telegram**, **Matrix**는 미리보기 스트리밍이 활성화된 경우 기본적으로 도구 진행을 실시간 미리보기 편집으로 스트리밍합니다. Microsoft Teams는 개인 채팅에서 네이티브 진행 스트림을 사용합니다. -- Telegram은 `v2026.4.22`부터 도구 진행 미리보기 업데이트가 활성화된 상태로 출시되었습니다. 이를 활성화 상태로 유지하면 릴리스된 동작이 보존됩니다. -- **Mattermost**는 이미 도구 활동을 단일 초안 미리보기 게시물에 포함합니다(위 참조). -- 도구 진행 편집은 활성 미리보기 스트리밍 모드를 따릅니다. 미리보기 스트리밍이 `off`이거나 블록 스트리밍이 메시지를 넘겨받은 경우 건너뜁니다. Telegram에서 `streaming.mode: "off"`는 최종 응답 전용입니다. 일반 진행 잡담도 독립 상태 메시지로 전달되지 않고 억제되지만, 승인 프롬프트, 미디어 페이로드, 오류는 계속 정상 라우팅됩니다. -- 미리보기 스트리밍은 유지하되 도구 진행 줄을 숨기려면 해당 채널의 `streaming.preview.toolProgress`를 `false`로 설정하세요. 미리보기 편집을 완전히 비활성화하려면 `streaming.mode`를 `off`로 설정하세요. -- Telegram 선택 인용 응답은 예외입니다. `replyToMode`가 `"off"`가 아니고 선택된 인용 텍스트가 있으면, OpenClaw는 해당 턴의 답변 미리보기 스트림을 건너뛰므로 도구 진행 미리보기 줄이 렌더링될 수 없습니다. 선택된 인용 텍스트가 없는 현재 메시지 응답은 미리보기 스트리밍을 계속 유지합니다. 자세한 내용은 [Telegram 채널 문서](/ko/channels/telegram)를 참조하세요. +- **Discord**, **Slack**, **Telegram**, **Matrix**는 미리보기 스트리밍이 활성화된 경우 기본적으로 도구 진행률을 실시간 미리보기 수정으로 스트리밍합니다. Microsoft Teams는 개인 채팅에서 네이티브 진행 스트림을 사용합니다. +- Telegram은 `v2026.4.22`부터 도구 진행률 미리보기 업데이트가 활성화된 상태로 출시되었습니다. 이를 활성화 상태로 유지하면 릴리스된 동작이 보존됩니다. +- **Mattermost**는 이미 도구 활동을 단일 초안 미리보기 게시물에 접어 넣습니다(위 참조). +- 도구 진행률 수정은 활성 미리보기 스트리밍 모드를 따릅니다. 미리보기 스트리밍이 `off`이거나 블록 스트리밍이 메시지를 대신 처리한 경우 건너뜁니다. Telegram에서 `streaming.mode: "off"`는 최종 전용입니다. 일반 진행 잡담도 독립형 상태 메시지로 전달되지 않고 억제되지만, 승인 프롬프트, 미디어 페이로드, 오류는 계속 정상적으로 라우팅됩니다. +- 미리보기 스트리밍은 유지하되 도구 진행률 줄을 숨기려면 해당 채널의 `streaming.preview.toolProgress`를 `false`로 설정합니다. 명령/실행 텍스트를 숨기면서 도구 진행률 줄을 계속 표시하려면 `streaming.preview.commandText`를 `"status"`로 설정하거나 `streaming.progress.commandText`를 `"status"`로 설정합니다. 릴리스된 동작을 보존하기 위해 기본값은 `"raw"`입니다. 이 정책은 Discord, Matrix, Microsoft Teams, Mattermost, Slack 초안 미리보기, Telegram을 포함하여 OpenClaw의 간결한 진행률 렌더러를 사용하는 초안/진행 채널에서 공유됩니다. 미리보기 수정을 완전히 비활성화하려면 `streaming.mode`를 `off`로 설정합니다. +- Telegram 선택 인용 답장은 예외입니다. `replyToMode`가 `"off"`가 아니고 선택된 인용 텍스트가 있으면, OpenClaw는 해당 턴의 답변 미리보기 스트림을 건너뛰므로 도구 진행률 미리보기 줄을 렌더링할 수 없습니다. 선택된 인용 텍스트가 없는 현재 메시지 답장은 미리보기 스트리밍을 계속 유지합니다. 자세한 내용은 [Telegram 채널 문서](/ko/channels/telegram)를 참조하세요. -예: +진행 상황 줄은 계속 표시하되 원시 명령/exec 텍스트는 숨깁니다. ```json { @@ -199,7 +216,26 @@ Matrix: "streaming": { "mode": "partial", "preview": { - "toolProgress": false + "toolProgress": true, + "commandText": "status" + } + } + } + } +} +``` + +동일한 구조를 다른 간결한 진행 상황 채널 키 아래에 사용합니다. 예를 들어 `channels.discord`, `channels.matrix`, `channels.msteams`, `channels.mattermost` 또는 Slack 초안 미리보기에 사용할 수 있습니다. 진행 상황 초안 모드의 경우 동일한 정책을 `streaming.progress` 아래에 넣습니다. + +```json +{ + "channels": { + "telegram": { + "streaming": { + "mode": "progress", + "progress": { + "toolProgress": true, + "commandText": "status" } } } @@ -209,7 +245,7 @@ Matrix: ## 관련 항목 -- [진행 상황 초안](/ko/concepts/progress-drafts) — 긴 턴 동안 업데이트되는 표시 가능한 진행 중 작업 메시지 -- [메시지](/ko/concepts/messages) — 메시지 수명 주기 및 전달 +- [진행 상황 초안](/ko/concepts/progress-drafts) — 긴 턴 동안 업데이트되는 표시 가능한 작업 진행 중 메시지 +- [메시지](/ko/concepts/messages) — 메시지 수명 주기와 전달 - [재시도](/ko/concepts/retry) — 전달 실패 시 재시도 동작 - [채널](/ko/channels) — 채널별 스트리밍 지원 diff --git a/docs/ko/help/testing.md b/docs/ko/help/testing.md index d59f3cfc0..98e575e9e 100644 --- a/docs/ko/help/testing.md +++ b/docs/ko/help/testing.md @@ -1,174 +1,144 @@ --- read_when: - 로컬 또는 CI에서 테스트 실행 - - 모델/프로바이더 버그에 대한 회귀 테스트 추가 + - 모델/제공자 버그에 대한 회귀 테스트 추가하기 - Gateway + 에이전트 동작 디버깅 -summary: '테스트 키트: 단위/e2e/라이브 스위트, Docker 러너, 그리고 각 테스트가 다루는 범위' +summary: '테스트 키트: unit/e2e/live 스위트, Docker 러너 및 각 테스트가 다루는 내용' title: 테스트 x-i18n: - generated_at: "2026-05-03T21:34:29Z" + generated_at: "2026-05-04T07:03:05Z" model: gpt-5.5 provider: openai - source_hash: e7fb57bee958c4e6243f02193a657d7b19ca633c7a27f70eac6b590931390671 + source_hash: ad724e3879d1d4dec21c4ea97e2fd5724c47269c1084c558a09f51bd72afc6a4 source_path: help/testing.md workflow: 16 --- -OpenClaw에는 세 가지 Vitest 스위트(유닛/통합, e2e, 라이브)와 소수의 -Docker 러너가 있습니다. 이 문서는 "우리가 테스트하는 방식" 가이드입니다: +OpenClaw에는 세 가지 Vitest 스위트(unit/integration, e2e, live)와 소수의 Docker 러너가 있습니다. 이 문서는 “테스트 방식” 가이드입니다. -- 각 스위트가 다루는 범위(그리고 의도적으로 다루지 _않는_ 범위). -- 일반적인 워크플로(로컬, 푸시 전, 디버깅)에서 실행할 명령. -- 라이브 테스트가 자격 증명을 찾고 모델/프로바이더를 선택하는 방식. -- 실제 모델/프로바이더 문제에 대한 회귀 테스트를 추가하는 방법. +- 각 스위트가 다루는 범위와 의도적으로 _다루지 않는_ 범위. +- 일반적인 워크플로(local, pre-push, debugging)에 실행할 명령. +- live 테스트가 자격 증명을 찾고 모델/프로바이더를 선택하는 방식. +- 실제 모델/프로바이더 문제에 대한 회귀 테스트를 추가하는 방식. -**QA 스택(qa-lab, qa-channel, 라이브 전송 레인)**은 별도로 문서화되어 있습니다: +**QA 스택(qa-lab, qa-channel, live transport lanes)** 은 별도로 문서화되어 있습니다. - [QA 개요](/ko/concepts/qa-e2e-automation) — 아키텍처, 명령 표면, 시나리오 작성. -- [Matrix QA](/ko/concepts/qa-matrix) — `pnpm openclaw qa matrix` 참조. +- [Matrix QA](/ko/concepts/qa-matrix) — `pnpm openclaw qa matrix`의 참조 문서. - [QA 채널](/ko/channels/qa-channel) — 저장소 기반 시나리오에서 사용하는 합성 전송 Plugin. -이 페이지는 일반 테스트 스위트와 Docker/Parallels 러너 실행을 다룹니다. 아래 QA별 러너 섹션([QA별 러너](#qa-specific-runners))에는 구체적인 `qa` 호출이 나열되어 있으며, 위 참조 문서로 다시 안내합니다. +이 페이지는 일반 테스트 스위트와 Docker/Parallels 러너 실행을 다룹니다. 아래 QA 전용 러너 섹션([QA 전용 러너](#qa-specific-runners))은 구체적인 `qa` 호출을 나열하고 위 참조 문서로 다시 안내합니다. ## 빠른 시작 -대부분의 날에는: +대부분의 경우: -- 전체 게이트(푸시 전에 기대됨): `pnpm build && pnpm check && pnpm check:test-types && pnpm test` -- 여유 있는 머신에서 더 빠른 로컬 전체 스위트 실행: `pnpm test:max` -- 직접 Vitest 감시 루프: `pnpm test:watch` -- 이제 직접 파일 대상 지정은 extension/channel 경로도 라우팅합니다: `pnpm test extensions/discord/src/monitor/message-handler.preflight.test.ts` -- 단일 실패를 반복 작업 중이라면 먼저 대상 지정 실행을 선호하세요. +- 전체 게이트(push 전 예상): `pnpm build && pnpm check && pnpm check:test-types && pnpm test` +- 여유 있는 머신에서 더 빠른 local 전체 스위트 실행: `pnpm test:max` +- 직접 Vitest watch 루프: `pnpm test:watch` +- 직접 파일 타기팅은 이제 확장/채널 경로도 라우팅합니다: `pnpm test extensions/discord/src/monitor/message-handler.preflight.test.ts` +- 단일 실패를 반복 수정하는 중이라면 먼저 타기팅된 실행을 선호하세요. - Docker 기반 QA 사이트: `pnpm qa:lab:up` - Linux VM 기반 QA 레인: `pnpm openclaw qa suite --runner multipass --scenario channel-chat-baseline` -테스트를 건드리거나 추가 확신이 필요할 때: +테스트를 건드렸거나 추가 확신이 필요할 때: - 커버리지 게이트: `pnpm test:coverage` - E2E 스위트: `pnpm test:e2e` 실제 프로바이더/모델을 디버깅할 때(실제 자격 증명 필요): -- 라이브 스위트(모델 + Gateway 도구/이미지 프로브): `pnpm test:live` -- 라이브 파일 하나를 조용히 대상으로 지정: `pnpm test:live -- src/agents/models.profiles.live.test.ts` -- 런타임 성능 보고서: 실제 `openai/gpt-5.4` 에이전트 턴에는 - `live_gpt54=true`, Kova CPU/힙/트레이스 아티팩트에는 - `deep_profile=true`로 `OpenClaw Performance`를 디스패치하세요. 일일 예약 실행은 - `CLAWGRIT_REPORTS_TOKEN`이 구성된 경우 mock-provider, deep-profile, GPT 5.4 레인 아티팩트를 - `openclaw/clawgrit-reports`에 게시합니다. - mock-provider 보고서에는 소스 수준 Gateway 부팅, 메모리, - plugin-pressure, 반복 fake-model hello-loop, CLI 시작 수치도 포함됩니다. -- Docker 라이브 모델 스윕: `pnpm test:docker:live-models` - - 선택된 각 모델은 이제 텍스트 턴과 작은 파일 읽기 스타일 프로브를 실행합니다. - 메타데이터가 `image` 입력을 알리는 모델은 작은 이미지 턴도 실행합니다. +- Live 스위트(모델 + Gateway 도구/이미지 프로브): `pnpm test:live` +- live 파일 하나를 조용히 타기팅: `pnpm test:live -- src/agents/models.profiles.live.test.ts` +- 런타임 성능 보고서: 실제 `openai/gpt-5.4` 에이전트 턴에는 `live_gpt54=true`, Kova CPU/heap/trace 아티팩트에는 `deep_profile=true`로 `OpenClaw Performance`를 디스패치합니다. 일일 예약 실행은 `CLAWGRIT_REPORTS_TOKEN`이 구성되어 있으면 mock-provider, deep-profile, GPT 5.4 레인 아티팩트를 `openclaw/clawgrit-reports`에 게시합니다. mock-provider 보고서에는 소스 수준 Gateway 부팅, 메모리, plugin-pressure, 반복 fake-model hello-loop, CLI 시작 시간도 포함됩니다. +- Docker live 모델 스윕: `pnpm test:docker:live-models` + - 선택한 각 모델은 이제 텍스트 턴과 작은 파일 읽기 스타일 프로브를 실행합니다. + 메타데이터가 `image` 입력을 광고하는 모델은 작은 이미지 턴도 실행합니다. 프로바이더 실패를 격리할 때는 `OPENCLAW_LIVE_MODEL_FILE_PROBE=0` 또는 - `OPENCLAW_LIVE_MODEL_IMAGE_PROBE=0`로 추가 프로브를 비활성화하세요. + `OPENCLAW_LIVE_MODEL_IMAGE_PROBE=0`으로 추가 프로브를 비활성화하세요. - CI 커버리지: 일일 `OpenClaw Scheduled Live And E2E Checks`와 수동 - `OpenClaw Release Checks`는 둘 다 재사용 가능한 라이브/E2E 워크플로를 - `include_live_suites: true`로 호출하며, 여기에는 프로바이더별로 샤딩된 별도 Docker 라이브 모델 - 매트릭스 작업이 포함됩니다. - - 집중 CI 재실행의 경우 `OpenClaw Live And E2E Checks (Reusable)`를 - `include_live_suites: true` 및 `live_models_only: true`로 디스패치하세요. - - 새 고신호 프로바이더 시크릿을 `scripts/ci-hydrate-live-auth.sh`에, - 그리고 `.github/workflows/openclaw-live-and-e2e-checks-reusable.yml` 및 해당 - 예약/릴리스 호출자에 추가하세요. -- 네이티브 Codex 바운드 채팅 스모크: `pnpm test:docker:live-codex-bind` - - Codex 앱 서버 경로에 대해 Docker 라이브 레인을 실행하고, 합성 - Slack DM을 `/codex bind`로 바인딩하며, `/codex fast`와 - `/codex permissions`를 실행한 뒤, 일반 답장과 이미지 첨부가 - ACP 대신 네이티브 Plugin 바인딩을 통해 라우팅되는지 확인합니다. -- Codex 앱 서버 하니스 스모크: `pnpm test:docker:live-codex-harness` - - Plugin 소유 Codex 앱 서버 하니스를 통해 Gateway 에이전트 턴을 실행하고, + `OpenClaw Release Checks`는 모두 `include_live_suites: true`로 재사용 가능한 live/E2E 워크플로를 호출하며, 여기에는 프로바이더별로 샤딩된 별도 Docker live 모델 매트릭스 작업이 포함됩니다. + - 집중 CI 재실행에는 `include_live_suites: true`와 `live_models_only: true`로 `OpenClaw Live And E2E Checks (Reusable)`를 디스패치하세요. + - 새로운 고신호 프로바이더 secret은 `scripts/ci-hydrate-live-auth.sh`와 `.github/workflows/openclaw-live-and-e2e-checks-reusable.yml` 및 해당 scheduled/release 호출자에 추가하세요. +- 네이티브 Codex bound-chat smoke: `pnpm test:docker:live-codex-bind` + - Codex app-server 경로를 대상으로 Docker live 레인을 실행하고, `/codex bind`로 합성 Slack DM을 바인딩하며, `/codex fast`와 + `/codex permissions`를 실행한 다음, 일반 응답과 이미지 첨부가 ACP 대신 네이티브 Plugin 바인딩을 통해 라우팅되는지 확인합니다. +- Codex app-server 하니스 smoke: `pnpm test:docker:live-codex-harness` + - Plugin 소유 Codex app-server 하니스를 통해 Gateway 에이전트 턴을 실행하고, `/codex status`와 `/codex models`를 검증하며, 기본적으로 이미지, - cron MCP, 하위 에이전트, Guardian 프로브를 실행합니다. 다른 Codex - 앱 서버 실패를 격리할 때는 `OPENCLAW_LIVE_CODEX_HARNESS_SUBAGENT_PROBE=0`으로 - 하위 에이전트 프로브를 비활성화하세요. 집중 하위 에이전트 검사의 경우 다른 프로브를 비활성화하세요: + Cron MCP, 하위 에이전트, Guardian 프로브를 실행합니다. 다른 Codex + app-server 실패를 격리할 때는 `OPENCLAW_LIVE_CODEX_HARNESS_SUBAGENT_PROBE=0`으로 하위 에이전트 프로브를 비활성화하세요. 집중 하위 에이전트 확인에는 다른 프로브를 비활성화하세요: `OPENCLAW_LIVE_CODEX_HARNESS_IMAGE_PROBE=0 OPENCLAW_LIVE_CODEX_HARNESS_MCP_PROBE=0 OPENCLAW_LIVE_CODEX_HARNESS_GUARDIAN_PROBE=0 OPENCLAW_LIVE_CODEX_HARNESS_SUBAGENT_PROBE=1 pnpm test:docker:live-codex-harness`. - 이는 `OPENCLAW_LIVE_CODEX_HARNESS_SUBAGENT_ONLY=0`이 설정되지 않는 한 - 하위 에이전트 프로브 후 종료됩니다. -- Crestodian 구조 명령 스모크: `pnpm test:live:crestodian-rescue-channel` - - 메시지 채널 구조 명령 표면을 위한 선택적 이중 확인 검사입니다. - `/crestodian status`를 실행하고, 지속 모델 변경을 큐에 넣고, - `/crestodian yes`로 답장한 뒤, 감사/구성 쓰기 경로를 검증합니다. -- Crestodian 플래너 Docker 스모크: `pnpm test:docker:crestodian-planner` - - `PATH`에 가짜 Claude CLI가 있는 구성 없는 컨테이너에서 Crestodian을 실행하고, - 퍼지 플래너 폴백이 감사된 typed config 쓰기로 변환되는지 검증합니다. -- Crestodian 최초 실행 Docker 스모크: `pnpm test:docker:crestodian-first-run` - - 빈 OpenClaw 상태 디렉터리에서 시작하고, bare `openclaw`를 - Crestodian으로 라우팅하며, setup/model/agent/Discord Plugin + SecretRef 쓰기를 적용하고, - 구성을 검증하고, 감사 항목을 확인합니다. 동일한 Ring 0 설정 경로는 - QA Lab에서도 `pnpm openclaw qa suite --scenario crestodian-ring-zero-setup`으로 - 커버됩니다. -- Moonshot/Kimi 비용 스모크: `MOONSHOT_API_KEY`가 설정된 상태에서 - `openclaw models list --provider moonshot --json`을 실행한 다음, - `moonshot/kimi-k2.6`에 대해 격리된 - `openclaw agent --local --session-id live-kimi-cost --message 'Reply exactly: KIMI_LIVE_OK' --thinking off --json`을 실행합니다. - JSON이 Moonshot/K2.6을 보고하고 어시스턴트 트랜스크립트가 정규화된 `usage.cost`를 저장하는지 확인하세요. + `OPENCLAW_LIVE_CODEX_HARNESS_SUBAGENT_ONLY=0`이 설정되지 않은 한 하위 에이전트 프로브 후 종료됩니다. +- Crestodian rescue 명령 smoke: `pnpm test:live:crestodian-rescue-channel` + - 메시지 채널 rescue 명령 표면에 대한 선택형 이중 안전 확인입니다. + `/crestodian status`를 실행하고, 영구 모델 변경을 큐에 넣고, + `/crestodian yes`에 응답한 뒤 audit/config 쓰기 경로를 검증합니다. +- Crestodian planner Docker smoke: `pnpm test:docker:crestodian-planner` + - `PATH`에 가짜 Claude CLI가 있는 configless 컨테이너에서 Crestodian을 실행하고, fuzzy planner fallback이 감사된 typed config 쓰기로 변환되는지 검증합니다. +- Crestodian first-run Docker smoke: `pnpm test:docker:crestodian-first-run` + - 빈 OpenClaw 상태 디렉터리에서 시작하고, bare `openclaw`를 Crestodian으로 라우팅하며, setup/model/agent/Discord Plugin + SecretRef 쓰기를 적용하고, config를 검증하며, audit 항목을 확인합니다. 동일한 Ring 0 설정 경로는 QA Lab에서도 `pnpm openclaw qa suite --scenario crestodian-ring-zero-setup`으로 다룹니다. +- Moonshot/Kimi 비용 smoke: `MOONSHOT_API_KEY`가 설정된 상태에서 + `openclaw models list --provider moonshot --json`을 실행한 다음, `moonshot/kimi-k2.6`을 대상으로 격리된 + `openclaw agent --local --session-id live-kimi-cost --message 'Reply exactly: KIMI_LIVE_OK' --thinking off --json`을 실행합니다. JSON이 Moonshot/K2.6을 보고하고 assistant transcript가 정규화된 `usage.cost`를 저장하는지 확인합니다. -실패 사례 하나만 필요할 때는 아래에 설명된 allowlist 환경 변수를 통해 라이브 테스트를 좁히는 방식을 선호하세요. +실패 사례 하나만 필요하다면 아래에 설명된 allowlist 환경 변수로 live 테스트를 좁히는 방식을 선호하세요. -## QA별 러너 +## QA 전용 러너 -QA-lab 수준의 현실성이 필요할 때 이 명령들은 주 테스트 스위트와 나란히 사용됩니다: +QA-lab 수준의 현실감이 필요할 때 이 명령들은 주요 테스트 스위트 옆에 있습니다. -CI는 전용 워크플로에서 QA Lab을 실행합니다. 에이전트형 패리티는 -독립 PR 워크플로가 아니라 `QA-Lab - All Lanes`와 릴리스 검증 아래에 중첩됩니다. -광범위한 검증에는 `rerun_group=qa-parity`가 있는 `Full Release Validation` 또는 -release-checks QA 그룹을 사용해야 합니다. `QA-Lab - All Lanes`는 -`main`에서 야간 실행되며, mock parity 레인, 라이브 -Matrix 레인, Convex 관리 라이브 Telegram 레인, Convex 관리 라이브 Discord -레인을 병렬 작업으로 포함해 수동 디스패치에서도 실행됩니다. 예약 QA와 릴리스 검사는 Matrix -`--profile fast`를 명시적으로 전달하지만, Matrix CLI와 수동 워크플로 입력의 -기본값은 `all`로 유지됩니다. 수동 디스패치는 `all`을 `transport`, +CI는 전용 워크플로에서 QA Lab을 실행합니다. Agentic parity는 독립 PR 워크플로가 아니라 +`QA-Lab - All Lanes` 및 릴리스 검증 아래에 중첩됩니다. +광범위한 검증에는 `rerun_group=qa-parity` 또는 release-checks QA 그룹을 사용한 `Full Release Validation`을 사용해야 합니다. `QA-Lab - All Lanes`는 `main`에서 야간 실행되고, 수동 디스패치에서는 mock parity 레인, live +Matrix 레인, Convex 관리 live Telegram 레인, Convex 관리 live Discord +레인을 병렬 작업으로 실행합니다. 예약 QA와 릴리스 확인은 Matrix +`--profile fast`를 명시적으로 전달하지만, Matrix CLI와 수동 워크플로 입력의 기본값은 `all`로 유지됩니다. 수동 디스패치는 `all`을 `transport`, `media`, `e2ee-smoke`, `e2ee-deep`, `e2ee-cli` 작업으로 샤딩할 수 있습니다. `OpenClaw Release -Checks`는 릴리스 승인 전에 패리티와 빠른 Matrix 및 Telegram 레인을 실행하며, -릴리스 전송 검사에는 `mock-openai/gpt-5.5`를 사용해 결정성을 유지하고 -일반 프로바이더 Plugin 시작을 피합니다. 이러한 라이브 전송 -Gateway는 메모리 검색을 비활성화합니다. 메모리 동작은 QA 패리티 -스위트에서 계속 커버됩니다. +Checks`는 릴리스 승인 전에 parity와 fast Matrix 및 Telegram 레인을 실행하며, 릴리스 전송 확인에는 `mock-openai/gpt-5.5`를 사용해 결정성을 유지하고 일반 프로바이더 Plugin 시작을 피합니다. 이러한 live transport +Gateway는 memory search를 비활성화합니다. 메모리 동작은 QA parity +스위트에서 계속 다룹니다. -전체 릴리스 라이브 미디어 샤드는 -`ghcr.io/openclaw/openclaw-live-media-runner:ubuntu-24.04`를 사용하며, 여기에는 이미 -`ffmpeg`와 `ffprobe`가 있습니다. Docker 라이브 모델/백엔드 샤드는 선택된 -커밋마다 한 번 빌드된 공유 -`ghcr.io/openclaw/openclaw-live-test:` 이미지를 사용한 다음, 각 샤드 안에서 다시 빌드하는 대신 -`OPENCLAW_SKIP_DOCKER_BUILD=1`로 이를 풀합니다. +전체 릴리스 live media 샤드는 이미 `ffmpeg`와 `ffprobe`가 포함된 +`ghcr.io/openclaw/openclaw-live-media-runner:ubuntu-24.04`를 사용합니다. Docker live model/backend 샤드는 선택한 커밋마다 한 번 빌드된 공유 +`ghcr.io/openclaw/openclaw-live-test:` 이미지를 사용한 다음, 각 샤드 내부에서 다시 빌드하지 않고 `OPENCLAW_SKIP_DOCKER_BUILD=1`로 가져옵니다. - `pnpm openclaw qa suite` - - 호스트에서 저장소 기반 QA 시나리오를 직접 실행합니다. - - 기본적으로 격리된 Gateway 워커를 사용해 선택된 여러 시나리오를 병렬로 실행합니다. `qa-channel`의 기본 동시성은 4입니다(선택된 시나리오 수로 제한됨). 워커 수를 조정하려면 `--concurrency `를 사용하고, 이전 직렬 레인에는 `--concurrency 1`을 사용하세요. - - 시나리오가 하나라도 실패하면 0이 아닌 코드로 종료합니다. 실패 종료 코드 없이 아티팩트가 필요할 때는 `--allow-failures`를 사용하세요. - - 공급자 모드 `live-frontier`, `mock-openai`, `aimock`을 지원합니다. `aimock`은 시나리오 인식 `mock-openai` 레인을 대체하지 않고, 실험적 fixture 및 프로토콜 mock 커버리지를 위해 로컬 AIMock 기반 공급자 서버를 시작합니다. + - 저장소 기반 QA 시나리오를 호스트에서 직접 실행합니다. + - 기본적으로 격리된 Gateway 워커를 사용해 선택된 여러 시나리오를 병렬로 실행합니다. `qa-channel`은 기본 동시성 4를 사용합니다(선택된 시나리오 수에 의해 제한됨). 워커 수를 조정하려면 `--concurrency `를 사용하고, 이전 직렬 레인에는 `--concurrency 1`을 사용합니다. + - 시나리오가 하나라도 실패하면 0이 아닌 코드로 종료합니다. 실패 종료 코드 없이 아티팩트가 필요할 때는 `--allow-failures`를 사용합니다. + - 제공자 모드 `live-frontier`, `mock-openai`, `aimock`를 지원합니다. `aimock`은 시나리오 인식 `mock-openai` 레인을 대체하지 않고, 실험적 픽스처와 프로토콜 목 커버리지를 위해 로컬 AIMock 기반 제공자 서버를 시작합니다. - `pnpm test:gateway:cpu-scenarios` - - Gateway 시작 벤치와 작은 mock QA Lab 시나리오 팩(`channel-chat-baseline`, `memory-failure-fallback`, `gateway-restart-inflight-run`)을 실행하고, `.artifacts/gateway-cpu-scenarios/` 아래에 결합된 CPU 관찰 요약을 작성합니다. - - 기본적으로 지속적인 고온 CPU 관찰만 플래그 처리하므로(`--cpu-core-warn` 및 `--hot-wall-warn-ms`), 짧은 시작 버스트는 수 분 동안 지속되는 Gateway 고정 회귀처럼 보이지 않고 메트릭으로 기록됩니다. - - 빌드된 `dist` 아티팩트를 사용합니다. 체크아웃에 최신 런타임 출력이 아직 없으면 먼저 빌드를 실행하세요. + - Gateway 시작 벤치와 작은 목 QA Lab 시나리오 팩(`channel-chat-baseline`, `memory-failure-fallback`, `gateway-restart-inflight-run`)을 실행하고, 결합된 CPU 관찰 요약을 `.artifacts/gateway-cpu-scenarios/` 아래에 작성합니다. + - 기본적으로 지속적인 고온 CPU 관찰만 플래그로 표시하므로(`--cpu-core-warn`와 `--hot-wall-warn-ms`), 짧은 시작 버스트는 몇 분 동안 Gateway가 고정되는 회귀처럼 보이지 않고 메트릭으로 기록됩니다. + - 빌드된 `dist` 아티팩트를 사용합니다. 체크아웃에 최신 런타임 출력이 아직 없으면 먼저 빌드를 실행합니다. - `pnpm openclaw qa suite --runner multipass` - - 폐기 가능한 Multipass Linux VM 안에서 동일한 QA 스위트를 실행합니다. + - 동일한 QA 제품군을 일회용 Multipass Linux VM 안에서 실행합니다. - 호스트의 `qa suite`와 동일한 시나리오 선택 동작을 유지합니다. - - `qa suite`와 동일한 공급자/모델 선택 플래그를 재사용합니다. - - 라이브 실행은 게스트에 실용적인 지원 QA 인증 입력을 전달합니다. env 기반 공급자 키, QA 라이브 공급자 설정 경로, 그리고 존재하는 경우 `CODEX_HOME`입니다. - - 출력 디렉터리는 게스트가 마운트된 워크스페이스를 통해 다시 쓸 수 있도록 저장소 루트 아래에 있어야 합니다. + - `qa suite`와 동일한 제공자/모델 선택 플래그를 재사용합니다. + - 라이브 실행은 게스트에서 실용적인 지원 QA 인증 입력을 전달합니다. env 기반 제공자 키, QA 라이브 제공자 구성 경로, 그리고 존재하는 경우 `CODEX_HOME`입니다. + - 출력 디렉터리는 마운트된 작업 공간을 통해 게스트가 다시 쓸 수 있도록 저장소 루트 아래에 있어야 합니다. - 일반 QA 보고서와 요약에 더해 Multipass 로그를 `.artifacts/qa-e2e/...` 아래에 작성합니다. - `pnpm qa:lab:up` - - 운영자 스타일 QA 작업을 위한 Docker 기반 QA 사이트를 시작합니다. + - 운영자식 QA 작업을 위해 Docker 기반 QA 사이트를 시작합니다. - `pnpm test:docker:npm-onboard-channel-agent` - - 현재 체크아웃에서 npm tarball을 빌드하고, Docker에 전역 설치한 뒤, 비대화형 OpenAI API 키 온보딩을 실행하고, 기본적으로 Telegram을 설정하며, 패키징된 Plugin 런타임이 시작 시 의존성 복구 없이 로드되는지 확인하고, doctor를 실행한 뒤, mock OpenAI 엔드포인트에 대해 로컬 agent 턴 하나를 실행합니다. - - Discord로 동일한 패키지 설치 레인을 실행하려면 `OPENCLAW_NPM_ONBOARD_CHANNEL=discord`를 사용하세요. + - 현재 체크아웃에서 npm 타볼을 빌드하고, Docker에 전역 설치한 뒤, 비대화형 OpenAI API 키 온보딩을 실행하고, 기본적으로 Telegram을 구성하며, 패키징된 Plugin 런타임이 시작 의존성 복구 없이 로드되는지 검증하고, doctor를 실행한 다음, 목 처리된 OpenAI 엔드포인트를 상대로 로컬 에이전트 턴 하나를 실행합니다. + - Discord로 동일한 패키징 설치 레인을 실행하려면 `OPENCLAW_NPM_ONBOARD_CHANNEL=discord`를 사용합니다. - `pnpm test:docker:session-runtime-context` - - 내장 런타임 컨텍스트 transcript를 위한 결정론적 빌드 앱 Docker smoke를 실행합니다. 숨겨진 OpenClaw 런타임 컨텍스트가 표시되는 사용자 턴으로 누출되지 않고 비표시 커스텀 메시지로 유지되는지 확인한 뒤, 영향을 받는 손상된 세션 JSONL을 시드하고 `openclaw doctor --fix`가 백업과 함께 이를 활성 브랜치로 다시 쓰는지 확인합니다. + - 임베디드 런타임 컨텍스트 transcript를 위한 결정적 빌드 앱 Docker 스모크를 실행합니다. 숨겨진 OpenClaw 런타임 컨텍스트가 표시되는 사용자 턴으로 누출되지 않고 비표시 사용자 지정 메시지로 유지되는지 검증한 다음, 영향을 받는 깨진 세션 JSONL을 시드하고 `openclaw doctor --fix`가 백업과 함께 활성 브랜치로 다시 작성하는지 검증합니다. - `pnpm test:docker:npm-telegram-live` - - Docker에 OpenClaw 패키지 후보를 설치하고, 설치된 패키지 온보딩을 실행하며, 설치된 CLI를 통해 Telegram을 설정한 다음, 해당 설치 패키지를 SUT Gateway로 사용해 라이브 Telegram QA 레인을 재사용합니다. - - 기본값은 `OPENCLAW_NPM_TELEGRAM_PACKAGE_SPEC=openclaw@beta`입니다. 레지스트리에서 설치하는 대신 해석된 로컬 tarball을 테스트하려면 `OPENCLAW_NPM_TELEGRAM_PACKAGE_TGZ=/path/to/openclaw-current.tgz` 또는 `OPENCLAW_CURRENT_PACKAGE_TGZ`를 설정하세요. - - `pnpm openclaw qa telegram`과 동일한 Telegram env 자격 증명 또는 Convex 자격 증명 소스를 사용합니다. CI/릴리스 자동화의 경우 `OPENCLAW_NPM_TELEGRAM_CREDENTIAL_SOURCE=convex`와 `OPENCLAW_QA_CONVEX_SITE_URL`, 그리고 역할 secret을 설정하세요. CI에 `OPENCLAW_QA_CONVEX_SITE_URL`과 Convex 역할 secret이 있으면 Docker wrapper가 Convex를 자동으로 선택합니다. + - Docker에 OpenClaw 패키지 후보를 설치하고, 설치된 패키지 온보딩을 실행하며, 설치된 CLI를 통해 Telegram을 구성한 뒤, 설치된 패키지를 SUT Gateway로 사용해 라이브 Telegram QA 레인을 재사용합니다. + - 기본값은 `OPENCLAW_NPM_TELEGRAM_PACKAGE_SPEC=openclaw@beta`입니다. 레지스트리에서 설치하지 않고 해석된 로컬 타볼을 테스트하려면 `OPENCLAW_NPM_TELEGRAM_PACKAGE_TGZ=/path/to/openclaw-current.tgz` 또는 `OPENCLAW_CURRENT_PACKAGE_TGZ`를 설정합니다. + - `pnpm openclaw qa telegram`과 동일한 Telegram env 자격 증명 또는 Convex 자격 증명 소스를 사용합니다. CI/릴리스 자동화에는 `OPENCLAW_NPM_TELEGRAM_CREDENTIAL_SOURCE=convex`와 `OPENCLAW_QA_CONVEX_SITE_URL` 및 역할 시크릿을 설정합니다. CI에 `OPENCLAW_QA_CONVEX_SITE_URL`과 Convex 역할 시크릿이 있으면 Docker 래퍼가 자동으로 Convex를 선택합니다. + - 래퍼는 Docker 빌드/설치 작업 전에 호스트에서 Telegram 또는 Convex 자격 증명 env를 검증합니다. 자격 증명 이전 설정을 의도적으로 디버그할 때만 `OPENCLAW_NPM_TELEGRAM_SKIP_CREDENTIAL_PREFLIGHT=1`을 설정합니다. - `OPENCLAW_NPM_TELEGRAM_CREDENTIAL_ROLE=ci|maintainer`는 이 레인에 대해서만 공유 `OPENCLAW_QA_CREDENTIAL_ROLE`을 재정의합니다. - - GitHub Actions는 이 레인을 수동 maintainer workflow `NPM Telegram Beta E2E`로 노출합니다. 병합 시 실행되지 않습니다. workflow는 `qa-live-shared` environment와 Convex CI 자격 증명 lease를 사용합니다. -- GitHub Actions는 후보 패키지 하나에 대한 side-run 제품 증명용 `Package Acceptance`도 노출합니다. 신뢰할 수 있는 ref, 게시된 npm spec, SHA-256이 있는 HTTPS tarball URL, 또는 다른 실행의 tarball artifact를 허용하고, 정규화된 `openclaw-current.tgz`를 `package-under-test`로 업로드한 다음, 기존 Docker E2E scheduler를 smoke, package, product, full, 또는 custom 레인 프로필로 실행합니다. 동일한 `package-under-test` 아티팩트에 대해 Telegram QA workflow를 실행하려면 `telegram_mode=mock-openai` 또는 `live-frontier`를 설정하세요. - - 최신 beta 제품 증명: + - GitHub Actions는 이 레인을 수동 유지 관리자 워크플로 `NPM Telegram Beta E2E`로 노출합니다. 병합 시에는 실행되지 않습니다. 이 워크플로는 `qa-live-shared` 환경과 Convex CI 자격 증명 임대를 사용합니다. +- GitHub Actions는 후보 패키지 하나에 대한 사이드 실행 제품 증거용으로 `Package Acceptance`도 노출합니다. 신뢰할 수 있는 ref, 게시된 npm 사양, SHA-256이 포함된 HTTPS 타볼 URL, 또는 다른 실행의 타볼 아티팩트를 받아 정규화된 `openclaw-current.tgz`를 `package-under-test`로 업로드한 다음, smoke, package, product, full 또는 사용자 지정 레인 프로필로 기존 Docker E2E 스케줄러를 실행합니다. 동일한 `package-under-test` 아티팩트를 상대로 Telegram QA 워크플로를 실행하려면 `telegram_mode=mock-openai` 또는 `live-frontier`를 설정합니다. + - 최신 베타 제품 증거: ```bash gh workflow run package-acceptance.yml --ref main \ @@ -178,7 +148,7 @@ gh workflow run package-acceptance.yml --ref main \ -f telegram_mode=mock-openai ``` -- 정확한 tarball URL 증명에는 digest가 필요합니다. +- 정확한 타볼 URL 증거에는 다이제스트가 필요합니다. ```bash gh workflow run package-acceptance.yml --ref main \ @@ -188,7 +158,7 @@ gh workflow run package-acceptance.yml --ref main \ -f suite_profile=package ``` -- Artifact 증명은 다른 Actions 실행에서 tarball artifact를 다운로드합니다. +- 아티팩트 증거는 다른 Actions 실행에서 타볼 아티팩트를 다운로드합니다. ```bash gh workflow run package-acceptance.yml --ref main \ @@ -199,56 +169,56 @@ gh workflow run package-acceptance.yml --ref main \ ``` - `pnpm test:docker:plugins` - - 현재 OpenClaw 빌드를 Docker에서 패키징하고 설치하며, OpenAI가 설정된 상태로 Gateway를 시작한 다음, config 편집을 통해 번들 채널/Plugin을 활성화합니다. - - 설정 탐색이 미설정 다운로드 가능 Plugin을 비워 두는지, 첫 번째 설정된 doctor 복구가 누락된 각 다운로드 가능 Plugin을 명시적으로 설치하는지, 두 번째 재시작이 숨겨진 의존성 복구를 실행하지 않는지 확인합니다. - - 또한 알려진 이전 npm baseline을 설치하고, `openclaw update --tag ` 실행 전에 Telegram을 활성화하며, 후보의 업데이트 후 doctor가 harness 측 postinstall 복구 없이 레거시 Plugin 의존성 잔해를 정리하는지 확인합니다. + - 현재 OpenClaw 빌드를 Docker에 패킹하고 설치하며, OpenAI가 구성된 상태로 Gateway를 시작한 다음, 구성 편집을 통해 번들 채널/Plugin을 활성화합니다. + - 설정 검색이 구성되지 않은 다운로드 가능 Plugin을 없는 상태로 남기는지, 첫 번째 구성된 doctor 복구가 누락된 각 다운로드 가능 Plugin을 명시적으로 설치하는지, 두 번째 재시작에서는 숨겨진 의존성 복구를 실행하지 않는지 검증합니다. + - 또한 알려진 이전 npm 기준선을 설치하고, `openclaw update --tag `를 실행하기 전에 Telegram을 활성화한 다음, 후보의 업데이트 후 doctor가 하네스 측 postinstall 복구 없이 레거시 Plugin 의존성 잔해를 정리하는지 검증합니다. - `pnpm test:parallels:npm-update` - - Parallels 게스트 전반에서 네이티브 패키지 설치 업데이트 smoke를 실행합니다. 선택된 각 플랫폼은 먼저 요청된 baseline 패키지를 설치한 뒤, 같은 게스트에서 설치된 `openclaw update` 명령을 실행하고 설치된 버전, 업데이트 상태, Gateway 준비 상태, 로컬 agent 턴 하나를 확인합니다. - - 게스트 하나를 반복 작업할 때는 `--platform macos`, `--platform windows`, 또는 `--platform linux`를 사용하세요. 요약 artifact 경로와 레인별 상태에는 `--json`을 사용하세요. - - OpenAI 레인은 기본적으로 라이브 agent 턴 증명에 `openai/gpt-5.5`를 사용합니다. 다른 OpenAI 모델을 의도적으로 검증할 때는 `--model `을 전달하거나 `OPENCLAW_PARALLELS_OPENAI_MODEL`을 설정하세요. - - Parallels 전송 정체가 나머지 테스트 시간을 소비하지 않도록 긴 로컬 실행은 호스트 timeout으로 감싸세요. + - Parallels 게스트 전반에서 네이티브 패키징 설치 업데이트 스모크를 실행합니다. 선택된 각 플랫폼은 먼저 요청된 기준 패키지를 설치한 다음, 같은 게스트에서 설치된 `openclaw update` 명령을 실행하고 설치된 버전, 업데이트 상태, Gateway 준비 상태, 로컬 에이전트 턴 하나를 검증합니다. + - 한 게스트에서 반복 작업할 때는 `--platform macos`, `--platform windows` 또는 `--platform linux`를 사용합니다. 요약 아티팩트 경로와 레인별 상태에는 `--json`을 사용합니다. + - OpenAI 레인은 기본적으로 라이브 에이전트 턴 증거에 `openai/gpt-5.5`를 사용합니다. 다른 OpenAI 모델을 의도적으로 검증할 때는 `--model `을 전달하거나 `OPENCLAW_PARALLELS_OPENAI_MODEL`을 설정합니다. + - Parallels 전송 정체가 나머지 테스트 시간을 소비하지 않도록 긴 로컬 실행을 호스트 타임아웃으로 감쌉니다. ```bash timeout --foreground 150m pnpm test:parallels:npm-update -- --json timeout --foreground 90m pnpm test:parallels:npm-update -- --platform windows --json ``` - - 스크립트는 `/tmp/openclaw-parallels-npm-update.*` 아래에 중첩 레인 로그를 작성합니다. 외부 wrapper가 멈췄다고 판단하기 전에 `windows-update.log`, `macos-update.log`, 또는 `linux-update.log`를 확인하세요. - - Windows 업데이트는 cold guest에서 업데이트 후 doctor 및 패키지 업데이트 작업에 10~15분을 사용할 수 있습니다. 중첩 npm debug 로그가 진행 중이면 여전히 정상입니다. - - 이 aggregate wrapper를 개별 Parallels macOS, Windows, 또는 Linux smoke 레인과 병렬로 실행하지 마세요. 이들은 VM 상태를 공유하며 snapshot restore, package serving, 또는 guest gateway 상태에서 충돌할 수 있습니다. - - 업데이트 후 증명은 일반 번들 Plugin surface를 실행합니다. speech, image generation, media understanding 같은 capability facade가, agent 턴 자체가 간단한 텍스트 응답만 확인하더라도 번들 런타임 API를 통해 로드되기 때문입니다. + - 스크립트는 중첩 레인 로그를 `/tmp/openclaw-parallels-npm-update.*` 아래에 작성합니다. 외부 래퍼가 멈췄다고 가정하기 전에 `windows-update.log`, `macos-update.log` 또는 `linux-update.log`를 검사합니다. + - Windows 업데이트는 콜드 게스트에서 업데이트 후 doctor와 패키지 업데이트 작업에 10~15분이 걸릴 수 있습니다. 중첩 npm 디버그 로그가 진행 중이면 여전히 정상입니다. + - 이 집계 래퍼를 개별 Parallels macOS, Windows 또는 Linux 스모크 레인과 병렬로 실행하지 않습니다. 이들은 VM 상태를 공유하며 스냅샷 복원, 패키지 서빙 또는 게스트 Gateway 상태에서 충돌할 수 있습니다. + - 업데이트 후 증거는 일반 번들 Plugin 표면을 실행합니다. 음성, 이미지 생성, 미디어 이해와 같은 기능 파사드는 에이전트 턴 자체가 단순 텍스트 응답만 확인하더라도 번들 런타임 API를 통해 로드되기 때문입니다. - `pnpm openclaw qa aimock` - - 직접 프로토콜 smoke 테스트를 위해 로컬 AIMock 공급자 서버만 시작합니다. + - 직접 프로토콜 스모크 테스트를 위해 로컬 AIMock 제공자 서버만 시작합니다. - `pnpm openclaw qa matrix` - - 폐기 가능한 Docker 기반 Tuwunel homeserver에 대해 Matrix 라이브 QA 레인을 실행합니다. 소스 체크아웃 전용입니다. 패키지 설치에는 `qa-lab`이 포함되지 않습니다. - - 전체 CLI, 프로필/시나리오 카탈로그, env vars, artifact 레이아웃: [Matrix QA](/ko/concepts/qa-matrix). + - 일회용 Docker 기반 Tuwunel 홈서버를 상대로 Matrix 라이브 QA 레인을 실행합니다. 소스 체크아웃 전용입니다. 패키징 설치에는 `qa-lab`이 포함되지 않습니다. + - 전체 CLI, 프로필/시나리오 카탈로그, env vars 및 아티팩트 레이아웃: [Matrix QA](/ko/concepts/qa-matrix). - `pnpm openclaw qa telegram` - - env의 driver 및 SUT bot token을 사용해 실제 private group에 대해 Telegram 라이브 QA 레인을 실행합니다. - - `OPENCLAW_QA_TELEGRAM_GROUP_ID`, `OPENCLAW_QA_TELEGRAM_DRIVER_BOT_TOKEN`, `OPENCLAW_QA_TELEGRAM_SUT_BOT_TOKEN`이 필요합니다. group id는 숫자 Telegram chat id여야 합니다. - - 공유 pooled credentials를 위해 `--credential-source convex`를 지원합니다. 기본적으로 env 모드를 사용하거나, pooled lease를 사용하려면 `OPENCLAW_QA_CREDENTIAL_SOURCE=convex`를 설정하세요. - - 시나리오가 하나라도 실패하면 0이 아닌 코드로 종료합니다. 실패 종료 코드 없이 아티팩트가 필요할 때는 `--allow-failures`를 사용하세요. - - 동일한 private group에 서로 다른 두 bot이 필요하며, SUT bot은 Telegram username을 노출해야 합니다. - - 안정적인 bot-to-bot 관찰을 위해 두 bot 모두에 대해 `@BotFather`에서 Bot-to-Bot Communication Mode를 활성화하고, driver bot이 group bot traffic을 관찰할 수 있는지 확인하세요. - - Telegram QA 보고서, 요약, observed-messages artifact를 `.artifacts/qa-e2e/...` 아래에 작성합니다. reply 시나리오에는 driver send request부터 관찰된 SUT reply까지의 RTT가 포함됩니다. + - env의 드라이버와 SUT 봇 토큰을 사용해 실제 비공개 그룹을 상대로 Telegram 라이브 QA 레인을 실행합니다. + - `OPENCLAW_QA_TELEGRAM_GROUP_ID`, `OPENCLAW_QA_TELEGRAM_DRIVER_BOT_TOKEN`, `OPENCLAW_QA_TELEGRAM_SUT_BOT_TOKEN`이 필요합니다. 그룹 id는 숫자 Telegram 채팅 id여야 합니다. + - 공유 풀링 자격 증명에는 `--credential-source convex`를 지원합니다. 기본적으로 env 모드를 사용하거나, 풀링 임대를 사용하려면 `OPENCLAW_QA_CREDENTIAL_SOURCE=convex`를 설정합니다. + - 시나리오가 하나라도 실패하면 0이 아닌 코드로 종료합니다. 실패 종료 코드 없이 아티팩트가 필요할 때는 `--allow-failures`를 사용합니다. + - 같은 비공개 그룹 안에 두 개의 서로 다른 봇이 필요하며, SUT 봇은 Telegram 사용자 이름을 노출해야 합니다. + - 안정적인 봇 간 관찰을 위해 두 봇 모두에 대해 `@BotFather`에서 Bot-to-Bot Communication Mode를 활성화하고, 드라이버 봇이 그룹 봇 트래픽을 관찰할 수 있는지 확인합니다. + - Telegram QA 보고서, 요약, 관찰된 메시지 아티팩트를 `.artifacts/qa-e2e/...` 아래에 작성합니다. 응답 시나리오에는 드라이버 전송 요청부터 관찰된 SUT 응답까지의 RTT가 포함됩니다. -라이브 전송 레인은 새 전송이 어긋나지 않도록 하나의 표준 contract를 공유합니다. 레인별 커버리지 매트릭스는 [QA overview → Live transport coverage](/ko/concepts/qa-e2e-automation#live-transport-coverage)에 있습니다. `qa-channel`은 광범위한 synthetic suite이며 해당 매트릭스의 일부가 아닙니다. +라이브 전송 레인은 새 전송이 어긋나지 않도록 하나의 표준 계약을 공유합니다. 레인별 커버리지 매트릭스는 [QA 개요 → 라이브 전송 커버리지](/ko/concepts/qa-e2e-automation#live-transport-coverage)에 있습니다. `qa-channel`은 광범위한 합성 제품군이며 이 매트릭스의 일부가 아닙니다. ### Convex를 통한 공유 Telegram 자격 증명(v1) -`openclaw qa telegram`에 대해 `--credential-source convex`(또는 `OPENCLAW_QA_CREDENTIAL_SOURCE=convex`)가 활성화되면 QA lab은 Convex 기반 pool에서 독점 lease를 획득하고, 레인이 실행되는 동안 해당 lease에 Heartbeat를 보내며, 종료 시 lease를 해제합니다. +`openclaw qa telegram`에 대해 `--credential-source convex`(또는 `OPENCLAW_QA_CREDENTIAL_SOURCE=convex`)가 활성화되면, QA lab은 Convex 기반 풀에서 독점 임대를 획득하고, 레인이 실행되는 동안 해당 임대에 Heartbeat를 보내며, 종료 시 임대를 해제합니다. -참조 Convex 프로젝트 scaffold: +참조 Convex 프로젝트 스캐폴드: - `qa/convex-credential-broker/` 필수 env vars: - `OPENCLAW_QA_CONVEX_SITE_URL`(예: `https://your-deployment.convex.site`) -- 선택된 role에 대한 secret 하나: +- 선택된 역할에 대한 시크릿 하나: - `maintainer`용 `OPENCLAW_QA_CONVEX_SECRET_MAINTAINER` - `ci`용 `OPENCLAW_QA_CONVEX_SECRET_CI` -- Credential role 선택: +- 자격 증명 역할 선택: - CLI: `--credential-role maintainer|ci` - Env 기본값: `OPENCLAW_QA_CREDENTIAL_ROLE`(CI에서는 기본값 `ci`, 그 외에는 `maintainer`) @@ -259,14 +229,14 @@ gh workflow run package-acceptance.yml --ref main \ - `OPENCLAW_QA_CREDENTIAL_ACQUIRE_TIMEOUT_MS`(기본값 `90000`) - `OPENCLAW_QA_CREDENTIAL_HTTP_TIMEOUT_MS`(기본값 `15000`) - `OPENCLAW_QA_CONVEX_ENDPOINT_PREFIX`(기본값 `/qa-credentials/v1`) -- `OPENCLAW_QA_CREDENTIAL_OWNER_ID`(선택적 trace id) -- `OPENCLAW_QA_ALLOW_INSECURE_HTTP=1`은 local-only development를 위해 loopback `http://` Convex URL을 허용합니다. +- `OPENCLAW_QA_CREDENTIAL_OWNER_ID`(선택적 추적 id) +- `OPENCLAW_QA_ALLOW_INSECURE_HTTP=1`은 로컬 전용 개발을 위해 loopback `http://` Convex URL을 허용합니다. -일반 운영에서는 `OPENCLAW_QA_CONVEX_SITE_URL`에 `https://`를 사용해야 합니다. +정상 작업에서는 `OPENCLAW_QA_CONVEX_SITE_URL`이 `https://`를 사용해야 합니다. -Maintainer admin commands(pool add/remove/list)에는 특히 `OPENCLAW_QA_CONVEX_SECRET_MAINTAINER`가 필요합니다. +유지 관리자 admin 명령(풀 추가/제거/목록)에는 특히 `OPENCLAW_QA_CONVEX_SECRET_MAINTAINER`가 필요합니다. -maintainer용 CLI helper: +유지 관리자를 위한 CLI 헬퍼: ```bash pnpm openclaw qa credentials doctor @@ -275,49 +245,51 @@ pnpm openclaw qa credentials list --kind telegram pnpm openclaw qa credentials remove --credential-id ``` -라이브 실행 전에 `doctor`를 사용해 Convex site URL, broker secrets, endpoint prefix, HTTP timeout, admin/list reachability를 secret 값을 출력하지 않고 확인하세요. scripts 및 CI 유틸리티에서 machine-readable 출력이 필요하면 `--json`을 사용하세요. +`doctor`를 사용해 라이브 실행 전에 Convex 사이트 URL, 브로커 시크릿, +엔드포인트 접두사, HTTP 타임아웃, admin/list 도달 가능성을 확인하되 +시크릿 값은 출력하지 않습니다. 스크립트와 CI 유틸리티에서 기계가 읽을 수 있는 출력이 필요하면 `--json`을 사용하세요. -기본 엔드포인트 계약(`OPENCLAW_QA_CONVEX_SITE_URL` + `/qa-credentials/v1`): +기본 엔드포인트 계약 (`OPENCLAW_QA_CONVEX_SITE_URL` + `/qa-credentials/v1`): - `POST /acquire` - 요청: `{ kind, ownerId, actorRole, leaseTtlMs, heartbeatIntervalMs }` - 성공: `{ status: "ok", credentialId, leaseToken, payload, leaseTtlMs?, heartbeatIntervalMs? }` - - 소진/재시도 가능: `{ status: "error", code: "POOL_EXHAUSTED" | "NO_CREDENTIAL_AVAILABLE", ... }` + - 고갈/재시도 가능: `{ status: "error", code: "POOL_EXHAUSTED" | "NO_CREDENTIAL_AVAILABLE", ... }` - `POST /heartbeat` - 요청: `{ kind, ownerId, actorRole, credentialId, leaseToken, leaseTtlMs }` - - 성공: `{ status: "ok" }`(또는 빈 `2xx`) + - 성공: `{ status: "ok" }` (또는 빈 `2xx`) - `POST /release` - 요청: `{ kind, ownerId, actorRole, credentialId, leaseToken }` - - 성공: `{ status: "ok" }`(또는 빈 `2xx`) -- `POST /admin/add`(관리자 비밀만) + - 성공: `{ status: "ok" }` (또는 빈 `2xx`) +- `POST /admin/add` (관리자 시크릿 전용) - 요청: `{ kind, actorId, payload, note?, status? }` - 성공: `{ status: "ok", credential }` -- `POST /admin/remove`(관리자 비밀만) +- `POST /admin/remove` (관리자 시크릿 전용) - 요청: `{ credentialId, actorId }` - 성공: `{ status: "ok", changed, credential }` - - 활성 임대 가드: `{ status: "error", code: "LEASE_ACTIVE", ... }` -- `POST /admin/list`(관리자 비밀만) + - 활성 임대 보호: `{ status: "error", code: "LEASE_ACTIVE", ... }` +- `POST /admin/list` (관리자 시크릿 전용) - 요청: `{ kind?, status?, includePayload?, limit? }` - 성공: `{ status: "ok", credentials, count }` -Telegram 종류의 페이로드 형식: +Telegram 종류의 페이로드 형태: - `{ groupId: string, driverToken: string, sutToken: string }` - `groupId`는 숫자로 된 Telegram 채팅 ID 문자열이어야 합니다. -- `admin/add`는 `kind: "telegram"`에 대해 이 형식을 검증하고 잘못된 페이로드를 거부합니다. +- `admin/add`는 `kind: "telegram"`에 대해 이 형태를 검증하고 잘못된 형식의 페이로드를 거부합니다. ### QA에 채널 추가하기 -새 채널 어댑터의 아키텍처와 시나리오 헬퍼 이름은 [QA 개요 → 채널 추가하기](/ko/concepts/qa-e2e-automation#adding-a-channel)에 있습니다. 최소 기준: 공유 `qa-lab` 호스트 경계에서 전송 러너를 구현하고, Plugin 매니페스트에 `qaRunners`를 선언하고, `openclaw qa `로 마운트하고, `qa/scenarios/` 아래에 시나리오를 작성합니다. +새 채널 어댑터의 아키텍처와 시나리오 헬퍼 이름은 [QA 개요 → 채널 추가하기](/ko/concepts/qa-e2e-automation#adding-a-channel)에 있습니다. 최소 기준은 공유 `qa-lab` 호스트 경계에서 트랜스포트 러너를 구현하고, Plugin 매니페스트에 `qaRunners`를 선언하고, `openclaw qa `로 마운트하고, `qa/scenarios/` 아래에 시나리오를 작성하는 것입니다. -## 테스트 스위트(어디에서 무엇이 실행되는가) +## 테스트 스위트(어디서 무엇이 실행되는가) -스위트는 “현실성이 높아지는 순서”(그리고 불안정성/비용도 증가하는 순서)라고 생각하세요. +스위트를 “현실성 증가”(그리고 불안정성/비용 증가)로 생각하세요. ### 단위 / 통합(기본값) - 명령: `pnpm test` -- 설정: 대상 지정 없는 실행은 `vitest.full-*.config.ts` 샤드 세트를 사용하며, 병렬 스케줄링을 위해 다중 프로젝트 샤드를 프로젝트별 설정으로 확장할 수 있습니다. +- 설정: 타깃이 지정되지 않은 실행은 `vitest.full-*.config.ts` 샤드 세트를 사용하며, 병렬 스케줄링을 위해 다중 프로젝트 샤드를 프로젝트별 설정으로 확장할 수 있습니다. - 파일: `src/**/*.test.ts`, `packages/**/*.test.ts`, `test/**/*.test.ts` 아래의 코어/단위 인벤토리. UI 단위 테스트는 전용 `unit-ui` 샤드에서 실행됩니다. - 범위: - 순수 단위 테스트 @@ -325,25 +297,26 @@ Telegram 종류의 페이로드 형식: - 알려진 버그에 대한 결정적 회귀 테스트 - 기대 사항: - CI에서 실행됨 - - 실제 키가 필요 없음 + - 실제 키가 필요하지 않음 - 빠르고 안정적이어야 함 - - 리졸버 및 공개 표면 로더 테스트는 실제 번들 Plugin 소스 API가 아니라 생성된 작은 Plugin 픽스처로 광범위한 `api.js` 및 + - 리졸버 및 공개 표면 로더 테스트는 실제 번들 Plugin 소스 API가 아니라 + 생성된 작은 Plugin 픽스처로 광범위한 `api.js` 및 `runtime-api.js` 폴백 동작을 증명해야 합니다. 실제 Plugin API 로드는 Plugin 소유 계약/통합 스위트에 속합니다. - + - - 대상 지정 없는 `pnpm test`는 하나의 거대한 네이티브 루트 프로젝트 프로세스 대신 열두 개의 더 작은 샤드 설정(`core-unit-fast`, `core-unit-src`, `core-unit-security`, `core-unit-ui`, `core-unit-support`, `core-support-boundary`, `core-contracts`, `core-bundled`, `core-runtime`, `agentic`, `auto-reply`, `extensions`)을 실행합니다. 이렇게 하면 부하가 걸린 머신에서 최대 RSS를 줄이고 auto-reply/extension 작업이 관련 없는 스위트를 굶기지 않게 합니다. + - 타깃이 지정되지 않은 `pnpm test`는 하나의 거대한 네이티브 루트 프로젝트 프로세스 대신 열두 개의 더 작은 샤드 설정(`core-unit-fast`, `core-unit-src`, `core-unit-security`, `core-unit-ui`, `core-unit-support`, `core-support-boundary`, `core-contracts`, `core-bundled`, `core-runtime`, `agentic`, `auto-reply`, `extensions`)을 실행합니다. 이렇게 하면 부하가 큰 머신에서 최대 RSS를 줄이고 auto-reply/확장 작업이 관련 없는 스위트를 굶기지 않도록 합니다. - `pnpm test --watch`는 여전히 네이티브 루트 `vitest.config.ts` 프로젝트 그래프를 사용합니다. 다중 샤드 감시 루프는 실용적이지 않기 때문입니다. - - `pnpm test`, `pnpm test:watch`, `pnpm test:perf:imports`는 명시적 파일/디렉터리 대상을 먼저 범위 지정된 레인으로 라우팅하므로 `pnpm test extensions/discord/src/monitor/message-handler.preflight.test.ts`가 전체 루트 프로젝트 시작 비용을 치르지 않습니다. - - `pnpm test:changed`는 기본적으로 변경된 git 경로를 저렴한 범위 지정 레인으로 확장합니다: 직접 테스트 편집, 형제 `*.test.ts` 파일, 명시적 소스 매핑, 로컬 import 그래프 의존 항목. 설정/셋업/패키지 편집은 명시적으로 `OPENCLAW_TEST_CHANGED_BROAD=1 pnpm test:changed`를 사용하지 않는 한 테스트를 넓게 실행하지 않습니다. - - `pnpm check:changed`는 좁은 작업을 위한 일반적인 스마트 로컬 검사 게이트입니다. diff를 코어, 코어 테스트, extensions, extension 테스트, 앱, 문서, 릴리스 메타데이터, 라이브 Docker 도구, 툴링으로 분류한 다음, 일치하는 타입체크, 린트, 가드 명령을 실행합니다. Vitest 테스트는 실행하지 않습니다. 테스트 증명에는 `pnpm test:changed` 또는 명시적 `pnpm test `를 호출하세요. 릴리스 메타데이터만 있는 버전 범프는 대상 지정 버전/설정/루트 의존성 검사를 실행하며, 최상위 버전 필드 밖의 패키지 변경을 거부하는 가드가 있습니다. - - 라이브 Docker ACP 하니스 편집은 집중 검사를 실행합니다: 라이브 Docker 인증 스크립트의 셸 문법과 라이브 Docker 스케줄러 드라이런. `package.json` 변경은 diff가 `scripts["test:docker:live-*"]`로 제한될 때만 포함됩니다. 의존성, export, 버전 및 기타 패키지 표면 편집은 여전히 더 넓은 가드를 사용합니다. - - agents, commands, plugins, auto-reply 헬퍼, `plugin-sdk` 및 유사한 순수 유틸리티 영역의 import가 가벼운 단위 테스트는 `unit-fast` 레인으로 라우팅되며, 이 레인은 `test/setup-openclaw-runtime.ts`를 건너뜁니다. 상태가 있거나 런타임이 무거운 파일은 기존 레인에 남습니다. + - `pnpm test`, `pnpm test:watch`, `pnpm test:perf:imports`는 명시적 파일/디렉터리 타깃을 먼저 범위 지정 레인으로 라우팅하므로, `pnpm test extensions/discord/src/monitor/message-handler.preflight.test.ts`는 전체 루트 프로젝트 시작 비용을 치르지 않습니다. + - `pnpm test:changed`는 변경된 git 경로를 기본적으로 저렴한 범위 지정 레인으로 확장합니다. 직접 테스트 편집, 형제 `*.test.ts` 파일, 명시적 소스 매핑, 로컬 import 그래프 의존 항목이 여기에 해당합니다. 설정/셋업/패키지 편집은 `OPENCLAW_TEST_CHANGED_BROAD=1 pnpm test:changed`를 명시적으로 사용하지 않는 한 테스트를 광범위하게 실행하지 않습니다. + - `pnpm check:changed`는 좁은 작업에 대한 일반적인 스마트 로컬 점검 게이트입니다. diff를 코어, 코어 테스트, 확장, 확장 테스트, 앱, 문서, 릴리스 메타데이터, 라이브 Docker 도구, 도구로 분류한 다음 해당 typecheck, lint, 가드 명령을 실행합니다. Vitest 테스트는 실행하지 않습니다. 테스트 증명이 필요하면 `pnpm test:changed` 또는 명시적 `pnpm test `을 호출하세요. 릴리스 메타데이터 전용 버전 범프는 타깃 버전/설정/루트 의존성 검사를 실행하며, 최상위 버전 필드 밖의 패키지 변경을 거부하는 가드를 포함합니다. + - 라이브 Docker ACP 하네스 편집은 집중 검사를 실행합니다. 라이브 Docker 인증 스크립트의 셸 문법과 라이브 Docker 스케줄러 드라이런입니다. diff가 `scripts["test:docker:live-*"]`로 제한될 때만 `package.json` 변경이 포함됩니다. 의존성, export, 버전, 기타 패키지 표면 편집은 여전히 더 넓은 가드를 사용합니다. + - 에이전트, 명령, Plugin, auto-reply 헬퍼, `plugin-sdk` 및 유사한 순수 유틸리티 영역의 import가 가벼운 단위 테스트는 `unit-fast` 레인으로 라우팅되며, 이 레인은 `test/setup-openclaw-runtime.ts`를 건너뜁니다. 상태가 있거나 런타임이 무거운 파일은 기존 레인에 남습니다. - 선택된 `plugin-sdk` 및 `commands` 헬퍼 소스 파일도 변경 모드 실행을 해당 가벼운 레인의 명시적 형제 테스트에 매핑하므로, 헬퍼 편집이 해당 디렉터리의 전체 무거운 스위트를 다시 실행하지 않습니다. - - `auto-reply`에는 최상위 코어 헬퍼, 최상위 `reply.*` 통합 테스트, `src/auto-reply/reply/**` 하위 트리에 대한 전용 버킷이 있습니다. CI는 reply 하위 트리를 agent-runner, dispatch, commands/state-routing 샤드로 추가 분할하여 import가 무거운 하나의 버킷이 전체 Node 꼬리를 독점하지 않게 합니다. - - 일반 PR/main CI는 의도적으로 extension 배치 스위프와 릴리스 전용 `agentic-plugins` 샤드를 건너뜁니다. 전체 릴리스 검증은 릴리스 후보에서 이러한 plugin/extension 비중이 큰 스위트를 위해 별도의 `Plugin Prerelease` 자식 워크플로를 디스패치합니다. + - `auto-reply`에는 최상위 코어 헬퍼, 최상위 `reply.*` 통합 테스트, `src/auto-reply/reply/**` 하위 트리에 대한 전용 버킷이 있습니다. CI는 reply 하위 트리를 에이전트 러너, 디스패치, 명령/상태 라우팅 샤드로 더 분할하여 import가 무거운 한 버킷이 전체 Node 꼬리 시간을 독점하지 않도록 합니다. + - 일반 PR/main CI는 의도적으로 확장 배치 스윕과 릴리스 전용 `agentic-plugins` 샤드를 건너뜁니다. Full Release Validation은 릴리스 후보에서 이러한 Plugin/확장 중심 스위트를 위해 별도의 `Plugin Prerelease` 자식 워크플로를 디스패치합니다. @@ -355,11 +328,9 @@ Telegram 종류의 페이로드 형식: 경계에 대한 집중 헬퍼 회귀 테스트를 추가하세요. - 임베디드 러너 통합 스위트를 건강하게 유지하세요: `src/agents/pi-embedded-runner/compact.hooks.test.ts`, - `src/agents/pi-embedded-runner/run.overflow-compaction.test.ts`, 및 + `src/agents/pi-embedded-runner/run.overflow-compaction.test.ts`, 그리고 `src/agents/pi-embedded-runner/run.overflow-compaction.loop.test.ts`. - - 이 스위트들은 범위 지정 ID와 Compaction 동작이 실제 `run.ts` / `compact.ts` 경로를 - 통해 계속 흐르는지 검증합니다. 헬퍼 전용 테스트는 - 이러한 통합 경로를 충분히 대체하지 못합니다. + - 이러한 스위트는 범위 지정 ID와 Compaction 동작이 실제 `run.ts` / `compact.ts` 경로를 통해 계속 흐르는지 검증합니다. 헬퍼 전용 테스트는 이러한 통합 경로를 충분히 대체하지 못합니다. @@ -372,7 +343,7 @@ Telegram 종류의 페이로드 형식: 공유 비격리 러너에서도 실행됩니다. - 각 `pnpm test` 샤드는 공유 Vitest 설정에서 동일한 `threads` + `isolate: false` 기본값을 상속합니다. - - `scripts/run-vitest.mjs`는 큰 로컬 실행 중 V8 컴파일 변동을 줄이기 위해 + - `scripts/run-vitest.mjs`는 대규모 로컬 실행 중 V8 컴파일 변동을 줄이기 위해 기본적으로 Vitest 자식 Node 프로세스에 `--no-maglev`를 추가합니다. 기본 V8 동작과 비교하려면 `OPENCLAW_VITEST_ENABLE_MAGLEV=1`을 설정하세요. @@ -381,23 +352,23 @@ Telegram 종류의 페이로드 형식: - `pnpm changed:lanes`는 diff가 어떤 아키텍처 레인을 트리거하는지 보여줍니다. - - pre-commit 훅은 포매팅 전용입니다. 포맷된 파일을 다시 stage하며 - 린트, 타입체크 또는 테스트를 실행하지 않습니다. - - 스마트 로컬 검사 게이트가 필요할 때는 인계 또는 푸시 전에 + - pre-commit 훅은 포매팅 전용입니다. 포매팅된 파일을 다시 스테이징하며 + lint, typecheck 또는 테스트를 실행하지 않습니다. + - 스마트 로컬 점검 게이트가 필요하면 핸드오프 또는 push 전에 `pnpm check:changed`를 명시적으로 실행하세요. - - `pnpm test:changed`는 기본적으로 저렴한 범위 지정 레인으로 라우팅됩니다. agent가 - 하니스, 설정, 패키지 또는 계약 편집에 정말 더 넓은 + - `pnpm test:changed`는 기본적으로 저렴한 범위 지정 레인을 통해 라우팅됩니다. 에이전트가 + 하네스, 설정, 패키지 또는 계약 편집에 실제로 더 넓은 Vitest 커버리지가 필요하다고 판단할 때만 `OPENCLAW_TEST_CHANGED_BROAD=1 pnpm test:changed`를 사용하세요. - - `pnpm test:max`와 `pnpm test:changed:max`는 동일한 라우팅 - 동작을 유지하되, 더 높은 워커 상한을 사용합니다. - - 로컬 워커 자동 스케일링은 의도적으로 보수적이며 호스트 로드 평균이 이미 높을 때 - 물러나므로, 여러 동시 - Vitest 실행이 기본적으로 피해를 덜 줍니다. + - `pnpm test:max`와 `pnpm test:changed:max`는 더 높은 워커 상한만 적용할 뿐 + 동일한 라우팅 동작을 유지합니다. + - 로컬 워커 자동 스케일링은 의도적으로 보수적이며, 호스트 load average가 이미 높으면 + 후퇴하므로 기본적으로 여러 동시 + Vitest 실행이 덜 해롭습니다. - 기본 Vitest 설정은 프로젝트/설정 파일을 - `forceRerunTriggers`로 표시하여 테스트 - 배선이 변경될 때 변경 모드 재실행이 정확하게 유지되도록 합니다. - - 설정은 지원되는 호스트에서 `OPENCLAW_VITEST_FS_MODULE_CACHE`를 활성화한 상태로 유지합니다. + `forceRerunTriggers`로 표시하므로 테스트 + 배선이 변경될 때 변경 모드 재실행이 올바르게 유지됩니다. + - 설정은 지원되는 호스트에서 `OPENCLAW_VITEST_FS_MODULE_CACHE`를 활성 상태로 유지합니다. 직접 프로파일링을 위해 명시적 캐시 위치 하나를 원하면 `OPENCLAW_VITEST_FS_MODULE_CACHE_PATH=/abs/path`를 설정하세요. @@ -407,25 +378,26 @@ Telegram 종류의 페이로드 형식: - `pnpm test:perf:imports`는 Vitest import-duration 보고와 import-breakdown 출력을 활성화합니다. - - `pnpm test:perf:imports:changed`는 동일한 프로파일링 뷰를 + - `pnpm test:perf:imports:changed`는 동일한 프로파일링 보기를 `origin/main` 이후 변경된 파일로 범위 지정합니다. - 샤드 타이밍 데이터는 `.artifacts/vitest-shard-timings.json`에 기록됩니다. 전체 설정 실행은 설정 경로를 키로 사용합니다. include-pattern CI - 샤드는 필터링된 샤드를 별도로 추적할 수 있도록 샤드 이름을 추가합니다. - - 하나의 핫 테스트가 여전히 대부분의 시간을 시작 import에 쓴다면, - 무거운 의존성을 좁은 로컬 `*.runtime.ts` 경계 뒤에 두고, - runtime 헬퍼를 `vi.mock(...)`에 전달하기 위해 deep-import하는 대신 - 그 경계를 직접 mock하세요. - - `pnpm test:perf:changed:bench -- --ref `는 해당 커밋된 - diff에 대해 라우팅된 `test:changed`를 네이티브 루트 프로젝트 경로와 비교하고 - 벽시계 시간과 macOS 최대 RSS를 출력합니다. + 샤드는 샤드 이름을 추가하여 필터링된 샤드를 + 별도로 추적할 수 있게 합니다. + - 뜨거운 테스트 하나가 여전히 대부분의 시간을 시작 import에 쓰고 있다면, + 무거운 의존성은 좁은 로컬 `*.runtime.ts` 경계 뒤에 두고 + 단순히 `vi.mock(...)`로 전달하기 위해 런타임 헬퍼를 deep import하지 말고 + 해당 경계를 직접 mock하세요. + - `pnpm test:perf:changed:bench -- --ref `는 해당 커밋 diff에 대해 라우팅된 + `test:changed`를 네이티브 루트 프로젝트 경로와 비교하고 + wall time과 macOS 최대 RSS를 출력합니다. - `pnpm test:perf:changed:bench -- --worktree`는 변경된 파일 목록을 `scripts/test-projects.mjs`와 루트 Vitest 설정을 통해 라우팅하여 현재 - 더티 트리를 벤치마크합니다. - - `pnpm test:perf:profile:main`은 - Vitest/Vite 시작 및 변환 오버헤드에 대한 메인 스레드 CPU 프로파일을 작성합니다. - - `pnpm test:perf:profile:runner`는 파일 병렬 처리를 비활성화한 상태에서 - 단위 스위트의 러너 CPU+heap 프로파일을 작성합니다. + dirty 트리를 벤치마크합니다. + - `pnpm test:perf:profile:main`은 Vitest/Vite 시작 및 transform 오버헤드에 대한 + 메인 스레드 CPU 프로파일을 기록합니다. + - `pnpm test:perf:profile:runner`는 파일 병렬 처리를 비활성화한 상태로 + 단위 스위트에 대한 러너 CPU+heap 프로파일을 기록합니다. @@ -433,144 +405,143 @@ Telegram 종류의 페이로드 형식: ### 안정성(Gateway) - 명령: `pnpm test:stability:gateway` -- 설정: `vitest.gateway.config.ts`, 워커 하나로 강제 +- 설정: `vitest.gateway.config.ts`, 한 워커로 강제됨 - 범위: - - 기본적으로 진단이 활성화된 실제 loopback Gateway를 시작합니다. - - 합성 Gateway 메시지, 메모리, 대형 페이로드 churn을 진단 이벤트 경로를 통해 구동합니다. + - 진단이 기본적으로 활성화된 실제 local loopback Gateway를 시작합니다. + - 진단 이벤트 경로를 통해 합성 Gateway 메시지, 메모리, 대용량 페이로드 변동을 구동합니다. - Gateway WS RPC를 통해 `diagnostics.stability`를 쿼리합니다. - - 진단 안정성 번들 지속성 헬퍼를 다룹니다. - - recorder가 제한된 범위에 머무르고, 합성 RSS 샘플이 압력 예산 아래에 있으며, 세션별 큐 깊이가 다시 0으로 drain되는지 단언합니다. + - 진단 안정성 번들 영속성 헬퍼를 포함합니다. + - 레코더가 한도 내에 유지되고, 합성 RSS 샘플이 pressure budget 아래에 머물며, 세션별 큐 깊이가 다시 0으로 비워지는지 단언합니다. - 기대 사항: - CI에 안전하고 키가 필요 없음 - - 안정성 회귀 후속 조치를 위한 좁은 레인이며, 전체 Gateway 스위트를 대체하지 않음 + - 안정성 회귀 후속 작업을 위한 좁은 레인이지, 전체 Gateway 스위트를 대체하지 않음 -### E2E(Gateway smoke) +### E2E(Gateway 스모크) - 명령: `pnpm test:e2e` -- 설정: `vitest.e2e.config.ts` -- 파일: `src/**/*.e2e.test.ts`, `test/**/*.e2e.test.ts`, 및 `extensions/` 아래의 번들 Plugin E2E 테스트 +- 구성: `vitest.e2e.config.ts` +- 파일: `src/**/*.e2e.test.ts`, `test/**/*.e2e.test.ts`, 그리고 `extensions/` 아래의 번들 Plugin E2E 테스트 - 런타임 기본값: - - 나머지 저장소와 일치하도록 Vitest `threads`와 `isolate: false`를 사용합니다. - - 적응형 워커를 사용합니다(CI: 최대 2, 로컬: 기본값 1). - - 콘솔 I/O 오버헤드를 줄이기 위해 기본적으로 silent 모드로 실행됩니다. + - 저장소의 나머지 부분과 동일하게 `isolate: false`로 Vitest `threads`를 사용합니다. + - 적응형 워커를 사용합니다(CI: 최대 2개, 로컬: 기본값 1개). + - 콘솔 I/O 오버헤드를 줄이기 위해 기본적으로 무음 모드로 실행됩니다. - 유용한 오버라이드: - - 워커 수를 강제하려면 `OPENCLAW_E2E_WORKERS=`(상한 16). - - 자세한 콘솔 출력을 다시 활성화하려면 `OPENCLAW_E2E_VERBOSE=1`. + - `OPENCLAW_E2E_WORKERS=`으로 워커 수를 강제합니다(최대 16개). + - `OPENCLAW_E2E_VERBOSE=1`로 자세한 콘솔 출력을 다시 활성화합니다. - 범위: - 다중 인스턴스 Gateway 종단 간 동작 - WebSocket/HTTP 표면, Node 페어링, 더 무거운 네트워킹 - 기대 사항: - - CI에서 실행됨(파이프라인에서 활성화된 경우) - - 실제 키가 필요 없음 - - 단위 테스트보다 움직이는 부분이 많음(더 느릴 수 있음) + - CI에서 실행됩니다(파이프라인에서 활성화된 경우). + - 실제 키가 필요하지 않습니다. + - 단위 테스트보다 움직이는 부분이 더 많습니다(더 느릴 수 있음). -### E2E: OpenShell 백엔드 smoke +### E2E: OpenShell 백엔드 스모크 - 명령: `pnpm test:e2e:openshell` - 파일: `extensions/openshell/src/backend.e2e.test.ts` - 범위: - - Docker를 통해 호스트에서 격리된 OpenShell Gateway를 시작합니다 - - 임시 로컬 Dockerfile에서 sandbox를 생성합니다 - - 실제 `sandbox ssh-config` + SSH exec를 통해 OpenClaw의 OpenShell 백엔드를 실행합니다 - - sandbox fs bridge를 통해 원격 정규 파일시스템 동작을 검증합니다 + - Docker를 통해 호스트에서 격리된 OpenShell Gateway를 시작합니다. + - 임시 로컬 Dockerfile에서 샌드박스를 생성합니다. + - 실제 `sandbox ssh-config` + SSH exec를 통해 OpenClaw의 OpenShell 백엔드를 실행합니다. + - 샌드박스 fs 브리지를 통해 원격 기준 파일 시스템 동작을 검증합니다. - 기대 사항: - - 명시적으로 선택해야만 실행됩니다. 기본 `pnpm test:e2e` 실행에는 포함되지 않습니다 - - 로컬 `openshell` CLI와 작동 중인 Docker daemon이 필요합니다 - - 격리된 `HOME` / `XDG_CONFIG_HOME`을 사용한 다음 테스트 Gateway와 sandbox를 삭제합니다 -- 유용한 재정의: - - 더 넓은 e2e 제품군을 수동으로 실행할 때 테스트를 활성화하려면 `OPENCLAW_E2E_OPENSHELL=1` - - 기본값이 아닌 CLI 바이너리나 wrapper script를 지정하려면 `OPENCLAW_E2E_OPENSHELL_COMMAND=/path/to/openshell` + - 옵트인 전용이며, 기본 `pnpm test:e2e` 실행에는 포함되지 않습니다. + - 로컬 `openshell` CLI와 동작하는 Docker 데몬이 필요합니다. + - 격리된 `HOME` / `XDG_CONFIG_HOME`을 사용한 뒤 테스트 Gateway와 샌드박스를 삭제합니다. +- 유용한 오버라이드: + - 더 넓은 e2e 스위트를 수동으로 실행할 때 테스트를 활성화하려면 `OPENCLAW_E2E_OPENSHELL=1`을 설정합니다. + - 기본값이 아닌 CLI 바이너리 또는 래퍼 스크립트를 가리키려면 `OPENCLAW_E2E_OPENSHELL_COMMAND=/path/to/openshell`을 설정합니다. -### 라이브 (실제 제공자 + 실제 모델) +### 라이브(실제 제공자 + 실제 모델) - 명령: `pnpm test:live` -- 설정: `vitest.live.config.ts` +- 구성: `vitest.live.config.ts` - 파일: `src/**/*.live.test.ts`, `test/**/*.live.test.ts`, 그리고 `extensions/` 아래의 번들 Plugin 라이브 테스트 -- 기본값: `pnpm test:live`에서 **활성화됨** (`OPENCLAW_LIVE_TEST=1` 설정) +- 기본값: `pnpm test:live`에 의해 **활성화됨**(`OPENCLAW_LIVE_TEST=1` 설정) - 범위: - “이 제공자/모델이 실제 자격 증명으로 _오늘_ 실제로 동작하는가?” - - 제공자 형식 변경, 도구 호출 특이점, 인증 문제, rate limit 동작을 포착합니다 + - 제공자 형식 변경, 도구 호출 특이점, 인증 문제, 속도 제한 동작 포착 - 기대 사항: - - 설계상 CI에서 안정적이지 않습니다(실제 네트워크, 실제 제공자 정책, 할당량, 장애) - - 비용이 발생하거나 rate limit을 사용합니다 - - “전부” 실행하는 대신 좁힌 하위 집합을 실행하는 것을 권장합니다 -- 라이브 실행은 누락된 API key를 가져오기 위해 `~/.profile`을 source합니다. -- 기본적으로 라이브 실행은 여전히 `HOME`을 격리하고 설정/인증 자료를 임시 테스트 home으로 복사하므로 단위 fixture가 실제 `~/.openclaw`를 변경할 수 없습니다. -- 라이브 테스트에서 실제 home directory를 의도적으로 사용해야 할 때만 `OPENCLAW_LIVE_USE_REAL_HOME=1`을 설정하세요. -- 이제 `pnpm test:live`는 더 조용한 모드가 기본값입니다. `[live] ...` 진행 출력은 유지하지만 추가 `~/.profile` 알림을 숨기고 Gateway bootstrap 로그/Bonjour chatter를 음소거합니다. 전체 시작 로그를 다시 보려면 `OPENCLAW_LIVE_TEST_QUIET=0`을 설정하세요. -- API key rotation(제공자별): 쉼표/세미콜론 형식의 `*_API_KEYS` 또는 `*_API_KEY_1`, `*_API_KEY_2`(예: `OPENAI_API_KEYS`, `ANTHROPIC_API_KEYS`, `GEMINI_API_KEYS`)를 설정하거나 `OPENCLAW_LIVE_*_KEY`로 라이브별 재정의를 설정하세요. 테스트는 rate limit 응답에서 재시도합니다. + - 설계상 CI에서 안정적이지 않습니다(실제 네트워크, 실제 제공자 정책, 할당량, 장애). + - 비용이 발생하거나 속도 제한을 사용합니다. + - “전체” 대신 좁힌 하위 집합 실행을 권장합니다. +- 라이브 실행은 누락된 API 키를 가져오기 위해 `~/.profile`을 소스로 읽습니다. +- 기본적으로 라이브 실행도 `HOME`을 격리하고 구성/인증 자료를 임시 테스트 홈으로 복사하므로 단위 fixture가 실제 `~/.openclaw`를 변경할 수 없습니다. +- 라이브 테스트가 의도적으로 실제 홈 디렉터리를 사용해야 할 때만 `OPENCLAW_LIVE_USE_REAL_HOME=1`을 설정하세요. +- `pnpm test:live`는 이제 더 조용한 모드를 기본값으로 사용합니다. `[live] ...` 진행 출력은 유지하지만, 추가 `~/.profile` 알림을 숨기고 Gateway 부트스트랩 로그/Bonjour 잡음을 음소거합니다. 전체 시작 로그를 다시 보려면 `OPENCLAW_LIVE_TEST_QUIET=0`을 설정하세요. +- API 키 순환(제공자별): `*_API_KEYS`를 쉼표/세미콜론 형식으로 설정하거나 `*_API_KEY_1`, `*_API_KEY_2`를 설정합니다(예: `OPENAI_API_KEYS`, `ANTHROPIC_API_KEYS`, `GEMINI_API_KEYS`). 또는 `OPENCLAW_LIVE_*_KEY`를 통해 라이브별 오버라이드를 설정합니다. 테스트는 속도 제한 응답 시 재시도합니다. - 진행/Heartbeat 출력: - - 라이브 제품군은 이제 provider 호출이 길어도 Vitest console capture가 조용할 때 stderr로 진행 줄을 출력해 실행 중임을 보이게 합니다. - - `vitest.live.config.ts`는 Vitest console interception을 비활성화하여 라이브 실행 중 provider/Gateway 진행 줄이 즉시 스트리밍되도록 합니다. - - 직접 모델 Heartbeat는 `OPENCLAW_LIVE_HEARTBEAT_MS`로 조정하세요. - - Gateway/probe Heartbeat는 `OPENCLAW_LIVE_GATEWAY_HEARTBEAT_MS`로 조정하세요. + - 이제 라이브 스위트는 긴 제공자 호출이 Vitest 콘솔 캡처가 조용한 상태에서도 눈에 띄게 활성 상태임을 보여주도록 진행 줄을 stderr로 내보냅니다. + - `vitest.live.config.ts`는 Vitest 콘솔 가로채기를 비활성화하여 라이브 실행 중 제공자/Gateway 진행 줄이 즉시 스트리밍되도록 합니다. + - 직접 모델 Heartbeat는 `OPENCLAW_LIVE_HEARTBEAT_MS`로 조정합니다. + - Gateway/프로브 Heartbeat는 `OPENCLAW_LIVE_GATEWAY_HEARTBEAT_MS`로 조정합니다. -## 어떤 제품군을 실행해야 하나요? +## 어떤 스위트를 실행해야 하나요? -이 결정 표를 사용하세요: +이 결정 표를 사용하세요. -- 로직/테스트 편집: `pnpm test`를 실행하세요(많이 변경했다면 `pnpm test:coverage`도 실행) -- Gateway 네트워킹 / WS protocol / pairing 수정: `pnpm test:e2e`를 추가하세요 -- “내 봇이 down됨” / 제공자별 실패 / 도구 호출 디버깅: 좁힌 `pnpm test:live`를 실행하세요 +- 로직/테스트 편집: `pnpm test`를 실행합니다(많이 변경했다면 `pnpm test:coverage`도 실행). +- Gateway 네트워킹 / WS 프로토콜 / 페어링을 건드림: `pnpm test:e2e`를 추가합니다. +- “내 봇이 내려갔다” / 제공자별 실패 / 도구 호출 디버깅: 좁힌 `pnpm test:live`를 실행합니다. -## 라이브(네트워크에 닿는) 테스트 +## 라이브(네트워크 접촉) 테스트 -라이브 모델 matrix, CLI backend smoke, ACP smoke, Codex app-server -harness, 그리고 모든 media-provider 라이브 테스트(Deepgram, BytePlus, ComfyUI, image, -music, video, media harness)와 라이브 실행의 credential handling은 -[라이브 제품군 테스트](/ko/help/testing-live)를 참조하세요. 전용 업데이트 및 -Plugin 검증 checklist는 +라이브 모델 매트릭스, CLI 백엔드 스모크, ACP 스모크, Codex 앱 서버 +하네스, 모든 미디어 제공자 라이브 테스트(Deepgram, BytePlus, ComfyUI, 이미지, +음악, 비디오, 미디어 하네스), 그리고 라이브 실행의 자격 증명 처리는 +[라이브 스위트 테스트](/ko/help/testing-live)를 참조하세요. 전용 업데이트 및 +Plugin 검증 체크리스트는 [업데이트 및 Plugin 테스트](/ko/help/testing-updates-plugins)를 참조하세요. -## Docker 실행기(선택 사항인 "Linux에서 동작함" 확인) +## Docker 러너(선택적 "Linux에서 동작" 확인) -이 Docker 실행기는 두 bucket으로 나뉩니다: +이 Docker 러너는 두 버킷으로 나뉩니다. -- 라이브 모델 실행기: `test:docker:live-models`와 `test:docker:live-gateway`는 repo Docker image 안에서 일치하는 profile-key live file만 실행합니다(`src/agents/models.profiles.live.test.ts` 및 `src/gateway/gateway-models.profiles.live.test.ts`). 이때 로컬 config dir와 workspace를 mount하고(mount된 경우 `~/.profile`도 source) 실행합니다. 일치하는 로컬 entrypoint는 `test:live:models-profiles`와 `test:live:gateway-profiles`입니다. -- Docker 라이브 실행기는 전체 Docker sweep을 현실적으로 유지하기 위해 더 작은 smoke cap을 기본값으로 사용합니다: +- 라이브 모델 러너: `test:docker:live-models`와 `test:docker:live-gateway`는 저장소 Docker 이미지 내부에서 일치하는 프로필 키 라이브 파일만 실행합니다(`src/agents/models.profiles.live.test.ts` 및 `src/gateway/gateway-models.profiles.live.test.ts`). 로컬 구성 디렉터리와 작업 공간을 마운트하고(마운트된 경우 `~/.profile`도 소스로 읽음) 실행합니다. 일치하는 로컬 진입점은 `test:live:models-profiles`와 `test:live:gateway-profiles`입니다. +- Docker 라이브 러너는 전체 Docker 스윕을 실용적으로 유지하기 위해 더 작은 스모크 제한을 기본값으로 사용합니다. `test:docker:live-models`의 기본값은 `OPENCLAW_LIVE_MAX_MODELS=12`이고, `test:docker:live-gateway`의 기본값은 `OPENCLAW_LIVE_GATEWAY_SMOKE=1`, `OPENCLAW_LIVE_GATEWAY_MAX_MODELS=8`, `OPENCLAW_LIVE_GATEWAY_STEP_TIMEOUT_MS=45000`, 그리고 - `OPENCLAW_LIVE_GATEWAY_MODEL_TIMEOUT_MS=90000`입니다. 더 큰 exhaustive scan을 - 명시적으로 원할 때 이 env var들을 재정의하세요. -- `test:docker:all`은 `test:docker:live-build`를 통해 라이브 Docker image를 한 번 build하고, `scripts/package-openclaw-for-docker.mjs`를 통해 OpenClaw를 npm tarball로 한 번 pack한 다음, 두 개의 `scripts/e2e/Dockerfile` image를 build/reuse합니다. bare image는 install/update/plugin-dependency lane을 위한 Node/Git runner일 뿐이며, 해당 lane들은 prebuilt tarball을 mount합니다. functional image는 built-app functionality lane을 위해 같은 tarball을 `/app`에 설치합니다. Docker lane 정의는 `scripts/lib/docker-e2e-scenarios.mjs`에 있고, planner logic은 `scripts/lib/docker-e2e-plan.mjs`에 있으며, `scripts/test-docker-all.mjs`가 선택된 plan을 실행합니다. aggregate는 weighted local scheduler를 사용합니다. `OPENCLAW_DOCKER_ALL_PARALLELISM`은 process slot을 제어하고, resource cap은 heavy live, npm-install, multi-service lane이 모두 동시에 시작되지 않게 합니다. 단일 lane이 active cap보다 무거워도 pool이 비어 있으면 scheduler가 여전히 시작할 수 있으며, 이후 capacity가 다시 사용 가능해질 때까지 단독으로 계속 실행합니다. 기본값은 10 slots, `OPENCLAW_DOCKER_ALL_LIVE_LIMIT=9`, `OPENCLAW_DOCKER_ALL_NPM_LIMIT=10`, `OPENCLAW_DOCKER_ALL_SERVICE_LIMIT=7`입니다. Docker host에 더 많은 headroom이 있을 때만 `OPENCLAW_DOCKER_ALL_WEIGHT_LIMIT` 또는 `OPENCLAW_DOCKER_ALL_DOCKER_LIMIT`를 조정하세요. runner는 기본적으로 Docker preflight를 수행하고, 오래된 OpenClaw E2E container를 제거하고, 30초마다 status를 출력하고, 성공한 lane timing을 `.artifacts/docker-tests/lane-timings.json`에 저장하며, 이후 실행에서 더 긴 lane을 먼저 시작하기 위해 해당 timing을 사용합니다. build나 Docker 실행 없이 weighted lane manifest를 출력하려면 `OPENCLAW_DOCKER_ALL_DRY_RUN=1`을 사용하거나, 선택된 lane, package/image 필요 항목, credentials에 대한 CI plan을 출력하려면 `node scripts/test-docker-all.mjs --plan-json`을 사용하세요. -- `Package Acceptance`는 "이 설치 가능한 tarball이 제품으로 동작하는가?"를 확인하는 GitHub-native package gate입니다. `source=npm`, `source=ref`, `source=url`, 또는 `source=artifact`에서 candidate package 하나를 resolve하고, 이를 `package-under-test`로 upload한 다음, 선택된 ref를 다시 pack하는 대신 정확히 그 tarball을 대상으로 reusable Docker E2E lane을 실행합니다. profile은 breadth 순서대로 `smoke`, `package`, `product`, `full`입니다. package/update/Plugin contract, published-upgrade survivor matrix, release defaults, failure triage는 [업데이트 및 Plugin 테스트](/ko/help/testing-updates-plugins)를 참조하세요. -- Build 및 release check는 tsdown 이후 `scripts/check-cli-bootstrap-imports.mjs`를 실행합니다. guard는 `dist/entry.js`와 `dist/cli/run-main.js`에서 static built graph를 순회하고, command dispatch 전에 pre-dispatch startup이 Commander, prompt UI, undici, logging 같은 package dependency를 import하면 실패합니다. 또한 bundled gateway run chunk를 budget 아래로 유지하고 알려진 cold gateway path의 static import를 거부합니다. Packaged CLI smoke도 root help, onboard help, doctor help, status, config schema, model-list command를 다룹니다. -- Package Acceptance legacy compatibility는 `2026.4.25`(`2026.4.25-beta.*` 포함)로 제한됩니다. 해당 cutoff까지 harness는 shipped-package metadata gap만 허용합니다. 즉 생략된 private QA inventory entry, 누락된 `gateway install --wrapper`, tarball-derived git fixture의 누락된 patch file, 누락된 persisted `update.channel`, legacy plugin install-record location, 누락된 marketplace install-record persistence, 그리고 `plugins update` 중 config metadata migration만 허용됩니다. `2026.4.25` 이후 package에서는 해당 path들이 strict failure입니다. -- Container smoke runner: `test:docker:openwebui`, `test:docker:onboard`, `test:docker:npm-onboard-channel-agent`, `test:docker:update-channel-switch`, `test:docker:upgrade-survivor`, `test:docker:published-upgrade-survivor`, `test:docker:session-runtime-context`, `test:docker:agents-delete-shared-workspace`, `test:docker:gateway-network`, `test:docker:browser-cdp-snapshot`, `test:docker:mcp-channels`, `test:docker:pi-bundle-mcp-tools`, `test:docker:cron-mcp-cleanup`, `test:docker:plugins`, `test:docker:plugin-update`, `test:docker:plugin-lifecycle-matrix`, 그리고 `test:docker:config-reload`는 하나 이상의 실제 container를 boot하고 더 높은 수준의 integration path를 검증합니다. + `OPENCLAW_LIVE_GATEWAY_MODEL_TIMEOUT_MS=90000`입니다. 더 큰 완전 스캔을 명시적으로 원할 때 해당 env var를 오버라이드하세요. +- `test:docker:all`은 `test:docker:live-build`를 통해 라이브 Docker 이미지를 한 번 빌드하고, `scripts/package-openclaw-for-docker.mjs`를 통해 OpenClaw를 npm tarball로 한 번 패키징한 다음, 두 개의 `scripts/e2e/Dockerfile` 이미지를 빌드/재사용합니다. 기본 이미지는 설치/업데이트/Plugin 의존성 레인용 Node/Git 러너일 뿐이며, 해당 레인은 미리 빌드된 tarball을 마운트합니다. 기능 이미지는 빌드된 앱 기능 레인을 위해 동일한 tarball을 `/app`에 설치합니다. Docker 레인 정의는 `scripts/lib/docker-e2e-scenarios.mjs`에 있으며, 플래너 로직은 `scripts/lib/docker-e2e-plan.mjs`에 있습니다. `scripts/test-docker-all.mjs`는 선택된 계획을 실행합니다. 집계는 가중치 기반 로컬 스케줄러를 사용합니다. `OPENCLAW_DOCKER_ALL_PARALLELISM`은 프로세스 슬롯을 제어하고, 리소스 제한은 무거운 라이브, npm 설치, 다중 서비스 레인이 모두 한 번에 시작되지 않도록 합니다. 단일 레인이 활성 제한보다 더 무겁더라도 풀이 비어 있으면 스케줄러가 시작할 수 있으며, 이후 용량을 다시 사용할 수 있을 때까지 단독 실행을 유지합니다. 기본값은 10개 슬롯, `OPENCLAW_DOCKER_ALL_LIVE_LIMIT=9`, `OPENCLAW_DOCKER_ALL_NPM_LIMIT=10`, `OPENCLAW_DOCKER_ALL_SERVICE_LIMIT=7`입니다. Docker 호스트에 더 많은 여유가 있을 때만 `OPENCLAW_DOCKER_ALL_WEIGHT_LIMIT` 또는 `OPENCLAW_DOCKER_ALL_DOCKER_LIMIT`를 조정하세요. 러너는 기본적으로 Docker 사전 점검을 수행하고, 오래된 OpenClaw E2E 컨테이너를 제거하며, 30초마다 상태를 출력하고, 성공한 레인 타이밍을 `.artifacts/docker-tests/lane-timings.json`에 저장한 뒤 이후 실행에서 더 긴 레인을 먼저 시작하는 데 해당 타이밍을 사용합니다. 빌드하거나 Docker를 실행하지 않고 가중치 적용 레인 매니페스트를 출력하려면 `OPENCLAW_DOCKER_ALL_DRY_RUN=1`을 사용하고, 선택된 레인, 패키지/이미지 필요 사항, 자격 증명에 대한 CI 계획을 출력하려면 `node scripts/test-docker-all.mjs --plan-json`을 사용하세요. +- `Package Acceptance`는 "설치 가능한 이 tarball이 제품으로 동작하는가?"에 대한 GitHub 네이티브 패키지 게이트입니다. `source=npm`, `source=ref`, `source=url`, 또는 `source=artifact`에서 하나의 후보 패키지를 해석하고, 이를 `package-under-test`로 업로드한 다음, 선택된 ref를 다시 패키징하는 대신 정확히 그 tarball에 대해 재사용 가능한 Docker E2E 레인을 실행합니다. 프로필은 범위 순서대로 `smoke`, `package`, `product`, `full`입니다. 패키지/업데이트/Plugin 계약, 게시된 업그레이드 생존자 매트릭스, 릴리스 기본값, 실패 트리아지는 [업데이트 및 Plugin 테스트](/ko/help/testing-updates-plugins)를 참조하세요. +- 빌드 및 릴리스 검사는 tsdown 이후 `scripts/check-cli-bootstrap-imports.mjs`를 실행합니다. 이 가드는 `dist/entry.js`와 `dist/cli/run-main.js`에서 정적 빌드 그래프를 순회하며, 명령 디스패치 전에 사전 디스패치 시작 imports가 Commander, 프롬프트 UI, undici, 로깅 같은 패키지 의존성을 가져오면 실패합니다. 또한 번들된 Gateway 실행 chunk를 예산 아래로 유지하고 알려진 콜드 Gateway 경로의 정적 import를 거부합니다. 패키징된 CLI 스모크는 루트 도움말, 온보드 도움말, doctor 도움말, 상태, 구성 스키마, 모델 목록 명령도 포함합니다. +- Package Acceptance 레거시 호환성은 `2026.4.25`(`2026.4.25-beta.*` 포함)에서 제한됩니다. 해당 cutoff까지 하네스는 출시된 패키지 메타데이터 공백만 허용합니다. 생략된 비공개 QA 인벤토리 항목, 누락된 `gateway install --wrapper`, tarball 파생 git fixture의 누락된 patch 파일, 누락된 지속 `update.channel`, 레거시 Plugin 설치 기록 위치, 누락된 marketplace 설치 기록 지속성, 그리고 `plugins update` 중 구성 메타데이터 마이그레이션입니다. `2026.4.25` 이후 패키지에서는 해당 경로가 엄격한 실패입니다. +- 컨테이너 스모크 러너: `test:docker:openwebui`, `test:docker:onboard`, `test:docker:npm-onboard-channel-agent`, `test:docker:update-channel-switch`, `test:docker:upgrade-survivor`, `test:docker:published-upgrade-survivor`, `test:docker:session-runtime-context`, `test:docker:agents-delete-shared-workspace`, `test:docker:gateway-network`, `test:docker:browser-cdp-snapshot`, `test:docker:mcp-channels`, `test:docker:pi-bundle-mcp-tools`, `test:docker:cron-mcp-cleanup`, `test:docker:plugins`, `test:docker:plugin-update`, `test:docker:plugin-lifecycle-matrix`, 그리고 `test:docker:config-reload`는 하나 이상의 실제 컨테이너를 부팅하고 상위 수준 통합 경로를 검증합니다. -라이브 모델 Docker 실행기는 필요한 CLI auth home만 bind-mount하거나(실행이 좁혀지지 않은 경우 지원되는 모든 home), 실행 전에 이를 container home으로 복사합니다. 이렇게 하면 external-CLI OAuth가 host auth store를 변경하지 않고 token을 refresh할 수 있습니다: +라이브 모델 Docker 러너는 필요한 CLI 인증 홈만(또는 실행이 좁혀지지 않은 경우 지원되는 모든 홈) bind-mount한 다음, 실행 전에 컨테이너 홈으로 복사하여 외부 CLI OAuth가 호스트 인증 저장소를 변경하지 않고 토큰을 갱신할 수 있게 합니다: -- 직접 모델: `pnpm test:docker:live-models`(스크립트: `scripts/test-live-models-docker.sh`) -- ACP 바인드 스모크: `pnpm test:docker:live-acp-bind`(스크립트: `scripts/test-live-acp-bind-docker.sh`; 기본적으로 Claude, Codex, Gemini를 포함하며, `pnpm test:docker:live-acp-bind:droid` 및 `pnpm test:docker:live-acp-bind:opencode`를 통해 엄격한 Droid/OpenCode 커버리지를 제공) -- CLI 백엔드 스모크: `pnpm test:docker:live-cli-backend`(스크립트: `scripts/test-live-cli-backend-docker.sh`) -- Codex 앱 서버 하네스 스모크: `pnpm test:docker:live-codex-harness`(스크립트: `scripts/test-live-codex-harness-docker.sh`) -- Gateway + 개발 에이전트: `pnpm test:docker:live-gateway`(스크립트: `scripts/test-live-gateway-models-docker.sh`) -- 관측 가능성 스모크: `pnpm qa:otel:smoke`는 비공개 QA 소스 체크아웃 레인입니다. npm 타르볼이 QA Lab을 생략하므로 의도적으로 패키지 Docker 릴리스 레인에 포함되지 않습니다. -- Open WebUI 라이브 스모크: `pnpm test:docker:openwebui`(스크립트: `scripts/e2e/openwebui-docker.sh`) -- 온보딩 마법사(TTY, 전체 스캐폴딩): `pnpm test:docker:onboard`(스크립트: `scripts/e2e/onboard-docker.sh`) -- Npm 타르볼 온보딩/채널/에이전트 스모크: `pnpm test:docker:npm-onboard-channel-agent`는 패키징된 OpenClaw 타르볼을 Docker에 전역 설치하고, env-ref 온보딩으로 OpenAI를 구성하며 기본적으로 Telegram도 구성하고, doctor를 실행한 뒤 모의 OpenAI 에이전트 턴 하나를 실행합니다. 미리 빌드된 타르볼을 `OPENCLAW_CURRENT_PACKAGE_TGZ=/path/to/openclaw-*.tgz`로 재사용하거나, `OPENCLAW_NPM_ONBOARD_HOST_BUILD=0`으로 호스트 재빌드를 건너뛰거나, `OPENCLAW_NPM_ONBOARD_CHANNEL=discord`로 채널을 전환하세요. -- 업데이트 채널 전환 스모크: `pnpm test:docker:update-channel-switch`는 패키징된 OpenClaw 타르볼을 Docker에 전역 설치하고, 패키지 `stable`에서 git `dev`로 전환하며, 유지된 채널과 Plugin 업데이트 후 동작을 확인한 다음, 다시 패키지 `stable`로 전환하고 업데이트 상태를 확인합니다. -- 업그레이드 생존자 스모크: `pnpm test:docker:upgrade-survivor`는 에이전트, 채널 구성, Plugin 허용 목록, 오래된 Plugin 의존성 상태, 기존 워크스페이스/세션 파일이 있는 더러운 이전 사용자 픽스처 위에 패키징된 OpenClaw 타르볼을 설치합니다. 라이브 제공자나 채널 키 없이 패키지 업데이트와 비대화형 doctor를 실행한 다음, loopback Gateway를 시작하고 구성/상태 보존 및 시작/상태 예산을 확인합니다. -- 게시된 업그레이드 생존자 스모크: `pnpm test:docker:published-upgrade-survivor`는 기본적으로 `openclaw@latest`를 설치하고, 현실적인 기존 사용자 파일을 시드하며, 내장된 명령 레시피로 해당 베이스라인을 구성하고, 결과 구성을 검증하고, 게시된 설치를 후보 타르볼로 업데이트하며, 비대화형 doctor를 실행하고, `.artifacts/upgrade-survivor/summary.json`을 작성한 다음, loopback Gateway를 시작하고 구성된 인텐트, 상태 보존, 시작, `/healthz`, `/readyz`, RPC 상태 예산을 확인합니다. 하나의 베이스라인을 `OPENCLAW_UPGRADE_SURVIVOR_BASELINE_SPEC`으로 재정의하거나, 집계 스케줄러에 `all-since-2026.4.23` 같은 `OPENCLAW_UPGRADE_SURVIVOR_BASELINE_SPECS`로 정확한 베이스라인을 확장하도록 요청하거나, `reported-issues` 같은 `OPENCLAW_UPGRADE_SURVIVOR_SCENARIOS`로 이슈 형태의 픽스처를 확장하세요. reported-issues 세트에는 외부 OpenClaw Plugin 설치를 자동 복구하기 위한 `configured-plugin-installs`가 포함됩니다. Package Acceptance는 이를 `published_upgrade_survivor_baseline`, `published_upgrade_survivor_baselines`, `published_upgrade_survivor_scenarios`로 노출합니다. -- 세션 런타임 컨텍스트 스모크: `pnpm test:docker:session-runtime-context`는 숨겨진 런타임 컨텍스트 transcript 지속성과 영향을 받은 중복 프롬프트 재작성 브랜치의 doctor 복구를 검증합니다. -- Bun 전역 설치 스모크: `bash scripts/e2e/bun-global-install-smoke.sh`는 현재 트리를 패키징하고, 격리된 홈에서 `bun install -g`로 설치하며, `openclaw infer image providers --json`이 중단되는 대신 번들 이미지 제공자를 반환하는지 검증합니다. 미리 빌드된 타르볼을 `OPENCLAW_BUN_GLOBAL_SMOKE_PACKAGE_TGZ=/path/to/openclaw-*.tgz`로 재사용하거나, `OPENCLAW_BUN_GLOBAL_SMOKE_HOST_BUILD=0`으로 호스트 빌드를 건너뛰거나, `OPENCLAW_BUN_GLOBAL_SMOKE_DIST_IMAGE=openclaw-dockerfile-smoke:local`로 빌드된 Docker 이미지에서 `dist/`를 복사하세요. -- 설치 프로그램 Docker 스모크: `bash scripts/test-install-sh-docker.sh`는 root, update, direct-npm 컨테이너 전체에서 하나의 npm 캐시를 공유합니다. 업데이트 스모크는 후보 타르볼로 업그레이드하기 전에 npm `latest`를 stable 베이스라인으로 기본 사용합니다. 로컬에서는 `OPENCLAW_INSTALL_SMOKE_UPDATE_BASELINE=2026.4.22`로, GitHub에서는 Install Smoke 워크플로의 `update_baseline_version` 입력으로 재정의하세요. non-root 설치 프로그램 검사는 격리된 npm 캐시를 유지하므로 root 소유 캐시 항목이 사용자 로컬 설치 동작을 가리지 않습니다. 로컬 재실행에서 root/update/direct-npm 캐시를 재사용하려면 `OPENCLAW_INSTALL_SMOKE_NPM_CACHE_DIR=/path/to/cache`를 설정하세요. -- Install Smoke CI는 `OPENCLAW_INSTALL_SMOKE_SKIP_NPM_GLOBAL=1`로 중복 direct-npm 전역 업데이트를 건너뜁니다. 직접 `npm install -g` 커버리지가 필요하면 해당 env 없이 스크립트를 로컬에서 실행하세요. -- 에이전트 공유 워크스페이스 삭제 CLI 스모크: `pnpm test:docker:agents-delete-shared-workspace`(스크립트: `scripts/e2e/agents-delete-shared-workspace-docker.sh`)는 기본적으로 루트 Dockerfile 이미지를 빌드하고, 격리된 컨테이너 홈에 하나의 워크스페이스를 공유하는 두 에이전트를 시드하며, `agents delete --json`을 실행하고, 유효한 JSON과 워크스페이스 유지 동작을 검증합니다. `OPENCLAW_AGENTS_DELETE_SHARED_WORKSPACE_E2E_IMAGE=openclaw-dockerfile-smoke:local OPENCLAW_AGENTS_DELETE_SHARED_WORKSPACE_E2E_SKIP_BUILD=1`로 install-smoke 이미지를 재사용하세요. -- Gateway 네트워킹(두 컨테이너, WS 인증 + 상태): `pnpm test:docker:gateway-network`(스크립트: `scripts/e2e/gateway-network-docker.sh`) -- 브라우저 CDP 스냅샷 스모크: `pnpm test:docker:browser-cdp-snapshot`(스크립트: `scripts/e2e/browser-cdp-snapshot-docker.sh`)는 소스 E2E 이미지와 Chromium 레이어를 빌드하고, 원시 CDP로 Chromium을 시작하며, `browser doctor --deep`을 실행하고, CDP 역할 스냅샷이 링크 URL, 커서 승격 클릭 가능 요소, iframe 참조, 프레임 메타데이터를 포함하는지 검증합니다. -- OpenAI Responses web_search 최소 reasoning 회귀: `pnpm test:docker:openai-web-search-minimal`(스크립트: `scripts/e2e/openai-web-search-minimal-docker.sh`)은 Gateway를 통해 모의 OpenAI 서버를 실행하고, `web_search`가 `reasoning.effort`를 `minimal`에서 `low`로 올리는지 검증한 다음, 제공자 스키마 거부를 강제하고 원시 detail이 Gateway 로그에 나타나는지 확인합니다. -- MCP 채널 브리지(시드된 Gateway + stdio 브리지 + 원시 Claude 알림 프레임 스모크): `pnpm test:docker:mcp-channels`(스크립트: `scripts/e2e/mcp-channels-docker.sh`) -- Pi 번들 MCP 도구(실제 stdio MCP 서버 + 내장 Pi 프로필 허용/거부 스모크): `pnpm test:docker:pi-bundle-mcp-tools`(스크립트: `scripts/e2e/pi-bundle-mcp-tools-docker.sh`) -- Cron/서브에이전트 MCP 정리(실제 Gateway + 격리된 cron 및 일회성 서브에이전트 실행 후 stdio MCP 자식 프로세스 정리): `pnpm test:docker:cron-mcp-cleanup`(스크립트: `scripts/e2e/cron-mcp-cleanup-docker.sh`) -- Plugin(로컬 경로, `file:`, 호이스팅된 의존성이 있는 npm registry, git 이동 참조, ClawHub kitchen-sink, marketplace 업데이트, Claude-bundle 활성화/검사의 설치/업데이트 스모크): `pnpm test:docker:plugins`(스크립트: `scripts/e2e/plugins-docker.sh`) - ClawHub 블록을 건너뛰려면 `OPENCLAW_PLUGINS_E2E_CLAWHUB=0`을 설정하거나, 기본 kitchen-sink 패키지/런타임 쌍을 `OPENCLAW_PLUGINS_E2E_CLAWHUB_SPEC` 및 `OPENCLAW_PLUGINS_E2E_CLAWHUB_ID`로 재정의하세요. `OPENCLAW_CLAWHUB_URL`/`CLAWHUB_URL`이 없으면 테스트는 hermetic 로컬 ClawHub 픽스처 서버를 사용합니다. -- Plugin 업데이트 변경 없음 스모크: `pnpm test:docker:plugin-update`(스크립트: `scripts/e2e/plugin-update-unchanged-docker.sh`) -- Plugin 수명 주기 매트릭스 스모크: `pnpm test:docker:plugin-lifecycle-matrix`는 기본 컨테이너에 패키징된 OpenClaw 타르볼을 설치하고, npm Plugin을 설치하며, 활성화/비활성화를 전환하고, 로컬 npm registry를 통해 업그레이드 및 다운그레이드하고, 설치된 코드를 삭제한 다음, 각 수명 주기 단계의 RSS/CPU 메트릭을 기록하면서 uninstall이 여전히 오래된 상태를 제거하는지 검증합니다. -- 구성 다시 로드 메타데이터 스모크: `pnpm test:docker:config-reload`(스크립트: `scripts/e2e/config-reload-source-docker.sh`) -- Plugin: `pnpm test:docker:plugins`는 로컬 경로, `file:`, 호이스팅된 의존성이 있는 npm registry, git 이동 참조, ClawHub 픽스처, marketplace 업데이트, Claude-bundle 활성화/검사의 설치/업데이트 스모크를 포함합니다. `pnpm test:docker:plugin-update`는 설치된 Plugin의 변경 없는 업데이트 동작을 포함합니다. `pnpm test:docker:plugin-lifecycle-matrix`는 리소스 추적 npm Plugin 설치, 활성화, 비활성화, 업그레이드, 다운그레이드, 누락 코드 uninstall을 포함합니다. +- 직접 모델: `pnpm test:docker:live-models` (스크립트: `scripts/test-live-models-docker.sh`) +- ACP 바인드 스모크: `pnpm test:docker:live-acp-bind` (스크립트: `scripts/test-live-acp-bind-docker.sh`; 기본적으로 Claude, Codex, Gemini를 다루며, `pnpm test:docker:live-acp-bind:droid` 및 `pnpm test:docker:live-acp-bind:opencode`를 통해 Droid/OpenCode를 엄격하게 포함) +- CLI 백엔드 스모크: `pnpm test:docker:live-cli-backend` (스크립트: `scripts/test-live-cli-backend-docker.sh`) +- Codex app-server 하네스 스모크: `pnpm test:docker:live-codex-harness` (스크립트: `scripts/test-live-codex-harness-docker.sh`) +- Gateway + 개발 에이전트: `pnpm test:docker:live-gateway` (스크립트: `scripts/test-live-gateway-models-docker.sh`) +- 관측 가능성 스모크: `pnpm qa:otel:smoke`는 비공개 QA 소스 체크아웃 레인입니다. npm tarball이 QA Lab을 제외하므로 의도적으로 패키지 Docker 릴리스 레인에 포함되지 않습니다. +- Open WebUI 라이브 스모크: `pnpm test:docker:openwebui` (스크립트: `scripts/e2e/openwebui-docker.sh`) +- 온보딩 마법사(TTY, 전체 스캐폴딩): `pnpm test:docker:onboard` (스크립트: `scripts/e2e/onboard-docker.sh`) +- Npm tarball 온보딩/채널/에이전트 스모크: `pnpm test:docker:npm-onboard-channel-agent`는 패키징된 OpenClaw tarball을 Docker에 전역 설치하고, env-ref 온보딩을 통해 OpenAI를 구성하며 기본적으로 Telegram도 구성하고, doctor를 실행한 뒤 mocked OpenAI 에이전트 턴을 하나 실행합니다. `OPENCLAW_CURRENT_PACKAGE_TGZ=/path/to/openclaw-*.tgz`로 미리 빌드한 tarball을 재사용하거나, `OPENCLAW_NPM_ONBOARD_HOST_BUILD=0`으로 호스트 재빌드를 건너뛰거나, `OPENCLAW_NPM_ONBOARD_CHANNEL=discord`로 채널을 전환하세요. +- 업데이트 채널 전환 스모크: `pnpm test:docker:update-channel-switch`는 패키징된 OpenClaw tarball을 Docker에 전역 설치하고, 패키지 `stable`에서 git `dev`로 전환하며, 유지된 채널과 Plugin 업데이트 후 동작을 검증한 다음, 다시 패키지 `stable`로 전환하고 업데이트 상태를 확인합니다. +- 업그레이드 생존자 스모크: `pnpm test:docker:upgrade-survivor`는 에이전트, 채널 구성, Plugin allowlist, 오래된 Plugin 의존성 상태, 기존 워크스페이스/세션 파일이 있는 더티 old-user fixture 위에 패키징된 OpenClaw tarball을 설치합니다. 라이브 제공자 또는 채널 키 없이 패키지 업데이트와 비대화형 doctor를 실행한 다음, loopback Gateway를 시작하고 구성/상태 보존 및 시작/상태 예산을 확인합니다. +- 게시된 업그레이드 생존자 스모크: `pnpm test:docker:published-upgrade-survivor`는 기본적으로 `openclaw@latest`를 설치하고, 현실적인 기존 사용자 파일을 시드하며, 내장된 명령 레시피로 해당 기준선을 구성하고, 결과 구성을 검증하고, 게시된 설치를 후보 tarball로 업데이트하고, 비대화형 doctor를 실행하고, `.artifacts/upgrade-survivor/summary.json`을 쓴 다음, loopback Gateway를 시작하고 구성된 intent, 상태 보존, 시작, `/healthz`, `/readyz`, RPC 상태 예산을 확인합니다. `OPENCLAW_UPGRADE_SURVIVOR_BASELINE_SPEC`로 기준선 하나를 재정의하고, `OPENCLAW_UPGRADE_SURVIVOR_BASELINE_SPECS`에 `all-since-2026.4.23` 같은 값을 지정해 집계 스케줄러가 정확한 기준선을 확장하도록 요청하며, `OPENCLAW_UPGRADE_SURVIVOR_SCENARIOS`에 `reported-issues` 같은 값을 지정해 이슈 형태의 fixture를 확장하세요. reported-issues 집합에는 자동 외부 OpenClaw Plugin 설치 복구를 위한 `configured-plugin-installs`가 포함됩니다. Package Acceptance는 이를 `published_upgrade_survivor_baseline`, `published_upgrade_survivor_baselines`, `published_upgrade_survivor_scenarios`로 노출합니다. +- 세션 런타임 컨텍스트 스모크: `pnpm test:docker:session-runtime-context`는 숨겨진 런타임 컨텍스트 transcript 지속성과 영향을 받은 중복 prompt-rewrite 브랜치에 대한 doctor 복구를 검증합니다. +- Bun 전역 설치 스모크: `bash scripts/e2e/bun-global-install-smoke.sh`는 현재 트리를 패키징하고, 격리된 홈에서 `bun install -g`로 설치한 뒤, `openclaw infer image providers --json`이 멈추지 않고 번들 이미지 제공자를 반환하는지 검증합니다. `OPENCLAW_BUN_GLOBAL_SMOKE_PACKAGE_TGZ=/path/to/openclaw-*.tgz`로 미리 빌드한 tarball을 재사용하거나, `OPENCLAW_BUN_GLOBAL_SMOKE_HOST_BUILD=0`으로 호스트 빌드를 건너뛰거나, `OPENCLAW_BUN_GLOBAL_SMOKE_DIST_IMAGE=openclaw-dockerfile-smoke:local`로 빌드된 Docker 이미지에서 `dist/`를 복사하세요. +- 설치 프로그램 Docker 스모크: `bash scripts/test-install-sh-docker.sh`는 root, update, direct-npm 컨테이너 전체에서 하나의 npm 캐시를 공유합니다. 업데이트 스모크는 후보 tarball로 업그레이드하기 전 stable 기준선으로 기본적으로 npm `latest`를 사용합니다. 로컬에서는 `OPENCLAW_INSTALL_SMOKE_UPDATE_BASELINE=2026.4.22`로 재정의하거나, GitHub에서는 Install Smoke 워크플로의 `update_baseline_version` 입력으로 재정의하세요. 비-root 설치 프로그램 검사는 root 소유 캐시 항목이 사용자 로컬 설치 동작을 가리지 않도록 격리된 npm 캐시를 유지합니다. 로컬 재실행 간에 root/update/direct-npm 캐시를 재사용하려면 `OPENCLAW_INSTALL_SMOKE_NPM_CACHE_DIR=/path/to/cache`를 설정하세요. +- Install Smoke CI는 `OPENCLAW_INSTALL_SMOKE_SKIP_NPM_GLOBAL=1`로 중복 direct-npm 전역 업데이트를 건너뜁니다. 직접 `npm install -g` 적용 범위가 필요하면 해당 env 없이 로컬에서 스크립트를 실행하세요. +- 에이전트 공유 워크스페이스 삭제 CLI 스모크: `pnpm test:docker:agents-delete-shared-workspace` (스크립트: `scripts/e2e/agents-delete-shared-workspace-docker.sh`)는 기본적으로 루트 Dockerfile 이미지를 빌드하고, 격리된 컨테이너 홈에 워크스페이스 하나를 가진 에이전트 두 개를 시드하고, `agents delete --json`을 실행한 뒤, 유효한 JSON과 워크스페이스 유지 동작을 검증합니다. `OPENCLAW_AGENTS_DELETE_SHARED_WORKSPACE_E2E_IMAGE=openclaw-dockerfile-smoke:local OPENCLAW_AGENTS_DELETE_SHARED_WORKSPACE_E2E_SKIP_BUILD=1`로 install-smoke 이미지를 재사용하세요. +- Gateway 네트워킹(컨테이너 2개, WS 인증 + health): `pnpm test:docker:gateway-network` (스크립트: `scripts/e2e/gateway-network-docker.sh`) +- Browser CDP 스냅샷 스모크: `pnpm test:docker:browser-cdp-snapshot` (스크립트: `scripts/e2e/browser-cdp-snapshot-docker.sh`)는 소스 E2E 이미지와 Chromium 레이어를 빌드하고, 원시 CDP로 Chromium을 시작하고, `browser doctor --deep`를 실행한 뒤, CDP 역할 스냅샷이 링크 URL, 커서로 승격된 클릭 가능 항목, iframe refs, frame 메타데이터를 포함하는지 검증합니다. +- OpenAI Responses web_search minimal reasoning 회귀: `pnpm test:docker:openai-web-search-minimal` (스크립트: `scripts/e2e/openai-web-search-minimal-docker.sh`)는 mocked OpenAI 서버를 Gateway를 통해 실행하고, `web_search`가 `reasoning.effort`를 `minimal`에서 `low`로 올리는지 검증한 다음, 제공자 스키마 거부를 강제하고 원시 detail이 Gateway 로그에 나타나는지 확인합니다. +- MCP 채널 브리지(시드된 Gateway + stdio 브리지 + 원시 Claude notification-frame 스모크): `pnpm test:docker:mcp-channels` (스크립트: `scripts/e2e/mcp-channels-docker.sh`) +- Pi 번들 MCP 도구(실제 stdio MCP 서버 + 내장 Pi 프로필 allow/deny 스모크): `pnpm test:docker:pi-bundle-mcp-tools` (스크립트: `scripts/e2e/pi-bundle-mcp-tools-docker.sh`) +- Cron/서브에이전트 MCP 정리(실제 Gateway + 격리된 cron 및 일회성 서브에이전트 실행 후 stdio MCP 자식 프로세스 정리): `pnpm test:docker:cron-mcp-cleanup` (스크립트: `scripts/e2e/cron-mcp-cleanup-docker.sh`) +- Plugins(로컬 경로, `file:`, 호이스트된 의존성이 있는 npm 레지스트리, git 이동 ref, ClawHub kitchen-sink, marketplace 업데이트, Claude-bundle 활성화/검사에 대한 설치/업데이트 스모크): `pnpm test:docker:plugins` (스크립트: `scripts/e2e/plugins-docker.sh`) + ClawHub 블록을 건너뛰려면 `OPENCLAW_PLUGINS_E2E_CLAWHUB=0`을 설정하거나, 기본 kitchen-sink 패키지/런타임 쌍을 `OPENCLAW_PLUGINS_E2E_CLAWHUB_SPEC` 및 `OPENCLAW_PLUGINS_E2E_CLAWHUB_ID`로 재정의하세요. `OPENCLAW_CLAWHUB_URL`/`CLAWHUB_URL`이 없으면 테스트는 hermetic 로컬 ClawHub fixture 서버를 사용합니다. +- Plugin 업데이트 변경 없음 스모크: `pnpm test:docker:plugin-update` (스크립트: `scripts/e2e/plugin-update-unchanged-docker.sh`) +- Plugin 수명 주기 매트릭스 스모크: `pnpm test:docker:plugin-lifecycle-matrix`는 패키징된 OpenClaw tarball을 빈 컨테이너에 설치하고, npm Plugin을 설치하고, 활성화/비활성화를 토글하고, 로컬 npm 레지스트리를 통해 업그레이드 및 다운그레이드하며, 설치된 코드를 삭제한 다음, 각 수명 주기 단계의 RSS/CPU 지표를 로깅하면서 uninstall이 여전히 오래된 상태를 제거하는지 검증합니다. +- 구성 reload 메타데이터 스모크: `pnpm test:docker:config-reload` (스크립트: `scripts/e2e/config-reload-source-docker.sh`) +- Plugins: `pnpm test:docker:plugins`는 로컬 경로, `file:`, 호이스트된 의존성이 있는 npm 레지스트리, git 이동 ref, ClawHub fixture, marketplace 업데이트, Claude-bundle 활성화/검사에 대한 설치/업데이트 스모크를 다룹니다. `pnpm test:docker:plugin-update`는 설치된 Plugins의 변경 없는 업데이트 동작을 다룹니다. `pnpm test:docker:plugin-lifecycle-matrix`는 리소스 추적 npm Plugin 설치, 활성화, 비활성화, 업그레이드, 다운그레이드, 코드 누락 uninstall을 다룹니다. 공유 기능 이미지를 수동으로 미리 빌드하고 재사용하려면: @@ -579,170 +550,168 @@ OPENCLAW_DOCKER_E2E_IMAGE=openclaw-docker-e2e-functional:local pnpm test:docker: OPENCLAW_DOCKER_E2E_IMAGE=openclaw-docker-e2e-functional:local OPENCLAW_SKIP_DOCKER_BUILD=1 pnpm test:docker:mcp-channels ``` -`OPENCLAW_GATEWAY_NETWORK_E2E_IMAGE` 같은 스위트별 이미지 재정의는 설정된 경우 여전히 우선합니다. `OPENCLAW_SKIP_DOCKER_BUILD=1`이 원격 공유 이미지를 가리키면, 스크립트는 해당 이미지가 로컬에 없을 때 pull합니다. QR 및 설치 프로그램 Docker 테스트는 공유 빌드 앱 런타임이 아니라 패키지/설치 동작을 검증하므로 자체 Dockerfile을 유지합니다. +설정된 경우 `OPENCLAW_GATEWAY_NETWORK_E2E_IMAGE` 같은 제품군별 이미지 재정의가 여전히 우선합니다. `OPENCLAW_SKIP_DOCKER_BUILD=1`이 원격 공유 이미지를 가리키면, 스크립트는 해당 이미지가 아직 로컬에 없을 때 이미지를 가져옵니다. QR 및 설치 프로그램 Docker 테스트는 공유 빌드 앱 런타임이 아니라 패키지/설치 동작을 검증하므로 자체 Dockerfile을 유지합니다. 라이브 모델 Docker 러너는 현재 체크아웃도 읽기 전용으로 바인드 마운트하고 컨테이너 내부의 임시 작업 디렉터리에 스테이징합니다. 이렇게 하면 런타임 -이미지를 작게 유지하면서도 정확히 로컬 소스/구성에 대해 Vitest를 실행할 수 있습니다. -스테이징 단계는 Docker 라이브 실행이 머신별 아티팩트를 복사하는 데 몇 분씩 -쓰지 않도록 `.pnpm-store`, `.worktrees`, `__openclaw_vitest__`, 앱 로컬 `.build` 또는 -Gradle 출력 디렉터리 같은 대용량 로컬 전용 캐시와 앱 빌드 출력을 건너뜁니다. -또한 `OPENCLAW_SKIP_CHANNELS=1`을 설정하므로 Gateway 라이브 프로브가 컨테이너 안에서 -실제 Telegram/Discord 등 채널 워커를 시작하지 않습니다. -`test:docker:live-models`는 여전히 `pnpm test:live`를 실행하므로, 해당 Docker 레인에서 -Gateway 라이브 커버리지를 좁히거나 제외해야 할 때는 `OPENCLAW_LIVE_GATEWAY_*`도 함께 -전달하세요. +이미지는 작게 유지하면서도 정확한 로컬 소스/설정에 대해 Vitest를 실행할 수 있습니다. +스테이징 단계는 `.pnpm-store`, `.worktrees`, `__openclaw_vitest__`, 앱 로컬 `.build` 또는 +Gradle 출력 디렉터리처럼 큰 로컬 전용 캐시와 앱 빌드 출력을 건너뛰어 Docker 라이브 실행이 +머신별 아티팩트를 복사하는 데 몇 분씩 쓰지 않도록 합니다. +또한 `OPENCLAW_SKIP_CHANNELS=1`을 설정하여 Gateway 라이브 프로브가 컨테이너 내부에서 +실제 Telegram/Discord/기타 채널 워커를 시작하지 않도록 합니다. +`test:docker:live-models`는 여전히 `pnpm test:live`를 실행하므로 해당 Docker 레인에서 +Gateway 라이브 커버리지를 좁히거나 제외해야 할 때는 `OPENCLAW_LIVE_GATEWAY_*`도 함께 전달하세요. `test:docker:openwebui`는 더 높은 수준의 호환성 스모크입니다. OpenAI 호환 HTTP 엔드포인트가 활성화된 OpenClaw Gateway 컨테이너를 시작하고, 해당 Gateway를 대상으로 고정된 Open WebUI -컨테이너를 시작하고, Open WebUI를 통해 로그인하고, `/api/models`가 `openclaw/default`를 -노출하는지 확인한 다음 Open WebUI의 `/api/chat/completions` 프록시를 통해 실제 채팅 요청을 -보냅니다. -첫 실행은 Docker가 Open WebUI 이미지를 가져와야 하고 Open WebUI가 자체 콜드 스타트 설정을 -마쳐야 할 수 있어서 눈에 띄게 느릴 수 있습니다. -이 레인은 사용할 수 있는 라이브 모델 키를 기대하며, `OPENCLAW_PROFILE_FILE` -(기본값: `~/.profile`)이 Docker화된 실행에서 이를 제공하는 기본 방법입니다. +컨테이너를 시작한 다음, Open WebUI를 통해 로그인하고, `/api/models`가 `openclaw/default`를 +노출하는지 확인한 뒤, Open WebUI의 `/api/chat/completions` 프록시를 통해 실제 채팅 요청을 보냅니다. +첫 실행은 Docker가 Open WebUI 이미지를 가져와야 하거나 Open WebUI가 자체 콜드 스타트 설정을 +끝내야 할 수 있어 눈에 띄게 느릴 수 있습니다. +이 레인은 사용할 수 있는 라이브 모델 키를 기대하며, Docker화된 실행에서 이를 제공하는 기본 방법은 +`OPENCLAW_PROFILE_FILE`(`~/.profile`이 기본값)입니다. 성공한 실행은 `{ "ok": true, "model": "openclaw/default", ... }` 같은 작은 JSON 페이로드를 출력합니다. -`test:docker:mcp-channels`는 의도적으로 결정적이며 실제 Telegram, Discord 또는 iMessage -계정이 필요하지 않습니다. 시드된 Gateway 컨테이너를 부팅하고, `openclaw mcp serve`를 생성하는 -두 번째 컨테이너를 시작한 다음, 실제 stdio MCP 브리지를 통해 라우팅된 대화 검색, 트랜스크립트 -읽기, 첨부 파일 메타데이터, 라이브 이벤트 큐 동작, 아웃바운드 전송 라우팅, Claude 스타일 채널 + -권한 알림을 검증합니다. 알림 검사는 원시 stdio MCP 프레임을 직접 검사하므로, 특정 클라이언트 SDK가 -우연히 노출하는 내용만이 아니라 브리지가 실제로 내보내는 내용을 스모크가 검증합니다. +`test:docker:mcp-channels`는 의도적으로 결정적이며 실제 Telegram, Discord 또는 iMessage 계정이 +필요하지 않습니다. 시드된 Gateway 컨테이너를 부팅하고, `openclaw mcp serve`를 생성하는 두 번째 +컨테이너를 시작한 다음, 실제 stdio MCP 브리지를 통해 라우팅된 대화 발견, 대화 기록 읽기, 첨부 파일 메타데이터, +라이브 이벤트 큐 동작, 아웃바운드 전송 라우팅, Claude 스타일 채널 + 권한 알림을 검증합니다. 알림 검사는 +원시 stdio MCP 프레임을 직접 검사하므로 특정 클라이언트 SDK가 우연히 표면화하는 것만이 아니라 +브리지가 실제로 내보내는 내용을 스모크가 검증합니다. `test:docker:pi-bundle-mcp-tools`는 결정적이며 라이브 모델 키가 필요하지 않습니다. repo Docker 이미지를 -빌드하고, 컨테이너 안에서 실제 stdio MCP 프로브 서버를 시작하고, 임베디드 Pi 번들 MCP 런타임을 통해 -그 서버를 구체화하고, 도구를 실행한 다음 `minimal` 및 `tools.deny: ["bundle-mcp"]`는 필터링하면서 -`coding`과 `messaging`은 `bundle-mcp` 도구를 유지하는지 검증합니다. -`test:docker:cron-mcp-cleanup`은 결정적이며 라이브 모델 키가 필요하지 않습니다. 실제 stdio MCP 프로브 -서버가 있는 시드된 Gateway를 시작하고, 격리된 cron 턴과 `/subagents spawn` 일회성 하위 턴을 실행한 -다음 각 실행 후 MCP 자식 프로세스가 종료되는지 검증합니다. +빌드하고, 컨테이너 내부에서 실제 stdio MCP 프로브 서버를 시작하고, 임베디드 Pi 번들 MCP 런타임을 통해 +해당 서버를 구체화하고, 도구를 실행한 다음, `coding` 및 `messaging`은 `bundle-mcp` 도구를 유지하고 +`minimal` 및 `tools.deny: ["bundle-mcp"]`는 이를 필터링하는지 검증합니다. +`test:docker:cron-mcp-cleanup`은 결정적이며 라이브 모델 키가 필요하지 않습니다. 실제 stdio MCP 프로브 서버가 +있는 시드된 Gateway를 시작하고, 격리된 Cron 턴과 `/subagents spawn` 일회성 자식 턴을 실행한 다음, +각 실행 후 MCP 자식 프로세스가 종료되는지 검증합니다. -수동 ACP 평문 스레드 스모크(CI 아님): +수동 ACP 일반 언어 스레드 스모크(CI 아님): - `bun scripts/dev/discord-acp-plain-language-smoke.ts --channel ...` -- 이 스크립트는 회귀/디버그 워크플로용으로 유지하세요. ACP 스레드 라우팅 검증에 다시 필요할 수 있으므로 삭제하지 마세요. +- 회귀/디버그 워크플로를 위해 이 스크립트를 유지하세요. ACP 스레드 라우팅 검증에 다시 필요할 수 있으므로 삭제하지 마세요. -유용한 env vars: +유용한 환경 변수: -- `OPENCLAW_CONFIG_DIR=...` (기본값: `~/.openclaw`) `/home/node/.openclaw`에 마운트됨 -- `OPENCLAW_WORKSPACE_DIR=...` (기본값: `~/.openclaw/workspace`) `/home/node/.openclaw/workspace`에 마운트됨 -- `OPENCLAW_PROFILE_FILE=...` (기본값: `~/.profile`) `/home/node/.profile`에 마운트되고 테스트 실행 전에 소싱됨 -- `OPENCLAW_DOCKER_PROFILE_ENV_ONLY=1`: 임시 구성/작업공간 디렉터리를 사용하고 외부 CLI 인증 마운트 없이 `OPENCLAW_PROFILE_FILE`에서 소싱된 env vars만 검증 -- `OPENCLAW_DOCKER_CLI_TOOLS_DIR=...` (기본값: `~/.cache/openclaw/docker-cli-tools`) Docker 내부 캐시된 CLI 설치를 위해 `/home/node/.npm-global`에 마운트됨 -- `$HOME` 아래의 외부 CLI 인증 디렉터리/파일은 `/host-auth...` 아래에 읽기 전용으로 마운트된 뒤, 테스트 시작 전에 `/home/node/...`로 복사됨 +- `/home/node/.openclaw`에 마운트되는 `OPENCLAW_CONFIG_DIR=...`(기본값: `~/.openclaw`) +- `/home/node/.openclaw/workspace`에 마운트되는 `OPENCLAW_WORKSPACE_DIR=...`(기본값: `~/.openclaw/workspace`) +- `/home/node/.profile`에 마운트되고 테스트 실행 전에 소싱되는 `OPENCLAW_PROFILE_FILE=...`(기본값: `~/.profile`) +- 임시 설정/작업공간 디렉터리를 사용하고 외부 CLI 인증 마운트 없이 `OPENCLAW_PROFILE_FILE`에서 소싱된 환경 변수만 검증하는 `OPENCLAW_DOCKER_PROFILE_ENV_ONLY=1` +- Docker 내부에서 캐시된 CLI 설치를 위해 `/home/node/.npm-global`에 마운트되는 `OPENCLAW_DOCKER_CLI_TOOLS_DIR=...`(기본값: `~/.cache/openclaw/docker-cli-tools`) +- `$HOME` 아래의 외부 CLI 인증 디렉터리/파일은 `/host-auth...` 아래에 읽기 전용으로 마운트된 다음, 테스트가 시작되기 전에 `/home/node/...`로 복사됩니다. - 기본 디렉터리: `.minimax` - 기본 파일: `~/.codex/auth.json`, `~/.codex/config.toml`, `.claude.json`, `~/.claude/.credentials.json`, `~/.claude/settings.json`, `~/.claude/settings.local.json` - - 좁혀진 provider 실행은 `OPENCLAW_LIVE_PROVIDERS` / `OPENCLAW_LIVE_GATEWAY_PROVIDERS`에서 추론한 필요한 디렉터리/파일만 마운트함 - - `OPENCLAW_DOCKER_AUTH_DIRS=all`, `OPENCLAW_DOCKER_AUTH_DIRS=none` 또는 `OPENCLAW_DOCKER_AUTH_DIRS=.claude,.codex` 같은 쉼표 목록으로 수동 재정의 -- `OPENCLAW_LIVE_GATEWAY_MODELS=...` / `OPENCLAW_LIVE_MODELS=...`: 실행 범위를 좁힘 -- `OPENCLAW_LIVE_GATEWAY_PROVIDERS=...` / `OPENCLAW_LIVE_PROVIDERS=...`: 컨테이너 내부 provider 필터링 -- `OPENCLAW_SKIP_DOCKER_BUILD=1`: 재빌드가 필요 없는 재실행에서 기존 `openclaw:local-live` 이미지 재사용 -- `OPENCLAW_LIVE_REQUIRE_PROFILE_KEYS=1`: 자격 증명이 env가 아니라 프로필 스토어에서 오도록 보장 -- `OPENCLAW_OPENWEBUI_MODEL=...`: Open WebUI 스모크용으로 Gateway가 노출하는 모델 선택 -- `OPENCLAW_OPENWEBUI_PROMPT=...`: Open WebUI 스모크가 사용하는 nonce 검사 프롬프트 재정의 -- `OPENWEBUI_IMAGE=...`: 고정된 Open WebUI 이미지 태그 재정의 + - 좁혀진 제공자 실행은 `OPENCLAW_LIVE_PROVIDERS` / `OPENCLAW_LIVE_GATEWAY_PROVIDERS`에서 추론된 필요한 디렉터리/파일만 마운트합니다. + - `OPENCLAW_DOCKER_AUTH_DIRS=all`, `OPENCLAW_DOCKER_AUTH_DIRS=none` 또는 `OPENCLAW_DOCKER_AUTH_DIRS=.claude,.codex` 같은 쉼표 목록으로 수동 재정의하세요. +- 실행을 좁히기 위한 `OPENCLAW_LIVE_GATEWAY_MODELS=...` / `OPENCLAW_LIVE_MODELS=...` +- 컨테이너 내부 제공자를 필터링하기 위한 `OPENCLAW_LIVE_GATEWAY_PROVIDERS=...` / `OPENCLAW_LIVE_PROVIDERS=...` +- 다시 빌드가 필요 없는 재실행에서 기존 `openclaw:local-live` 이미지를 재사용하기 위한 `OPENCLAW_SKIP_DOCKER_BUILD=1` +- 자격 증명이 환경 변수가 아니라 프로필 저장소에서 오도록 보장하는 `OPENCLAW_LIVE_REQUIRE_PROFILE_KEYS=1` +- Open WebUI 스모크를 위해 Gateway가 노출하는 모델을 선택하는 `OPENCLAW_OPENWEBUI_MODEL=...` +- Open WebUI 스모크에서 사용하는 nonce 검사 프롬프트를 재정의하는 `OPENCLAW_OPENWEBUI_PROMPT=...` +- 고정된 Open WebUI 이미지 태그를 재정의하는 `OPENWEBUI_IMAGE=...` -## 문서 정상성 +## 문서 무결성 검사 문서 편집 후 문서 검사를 실행하세요: `pnpm check:docs`. -페이지 내 heading 검사도 필요할 때는 전체 Mintlify 앵커 검증을 실행하세요: `pnpm docs:check-links:anchors`. +페이지 내 heading 검사까지 필요할 때는 전체 Mintlify 앵커 검증을 실행하세요: `pnpm docs:check-links:anchors`. ## 오프라인 회귀(CI 안전) -이들은 실제 provider 없이 수행하는 “실제 파이프라인” 회귀입니다. +다음은 실제 제공자 없이 실행되는 “실제 파이프라인” 회귀입니다. -- Gateway 도구 호출(mock OpenAI, 실제 Gateway + agent 루프): `src/gateway/gateway.test.ts` (케이스: "runs a mock OpenAI tool call end-to-end via gateway agent loop") -- Gateway 마법사(WS `wizard.start`/`wizard.next`, 구성 작성 + 인증 적용): `src/gateway/gateway.test.ts` (케이스: "runs wizard over ws and writes auth token config") +- Gateway 도구 호출(mock OpenAI, 실제 Gateway + 에이전트 루프): `src/gateway/gateway.test.ts`(케이스: "runs a mock OpenAI tool call end-to-end via gateway agent loop") +- Gateway 마법사(WS `wizard.start`/`wizard.next`, 설정 작성 + 인증 강제): `src/gateway/gateway.test.ts`(케이스: "runs wizard over ws and writes auth token config") -## Agent 신뢰성 evals(Skills) +## 에이전트 신뢰성 평가(Skills) -이미 “agent 신뢰성 evals”처럼 동작하는 CI 안전 테스트가 몇 가지 있습니다. +이미 “에이전트 신뢰성 평가”처럼 동작하는 CI 안전 테스트가 몇 가지 있습니다. -- 실제 Gateway + agent 루프를 통한 mock 도구 호출(`src/gateway/gateway.test.ts`). -- 세션 배선과 구성 효과를 검증하는 엔드투엔드 마법사 흐름(`src/gateway/gateway.test.ts`). +- 실제 Gateway + 에이전트 루프를 통한 mock 도구 호출(`src/gateway/gateway.test.ts`). +- 세션 배선과 설정 효과를 검증하는 엔드투엔드 마법사 플로(`src/gateway/gateway.test.ts`). -Skills에 아직 빠진 항목([Skills](/ko/tools/skills) 참고): +Skills에서 아직 누락된 것([Skills](/ko/tools/skills) 참고): -- **의사결정:** 프롬프트에 Skills가 나열되었을 때 agent가 올바른 skill을 선택하는가(또는 관련 없는 것을 피하는가)? -- **준수:** agent가 사용 전에 `SKILL.md`를 읽고 필수 단계/인자를 따르는가? -- **워크플로 계약:** 도구 순서, 세션 기록 이월, 샌드박스 경계를 검증하는 멀티턴 시나리오. +- **의사결정:** 프롬프트에 Skills가 나열되었을 때 에이전트가 올바른 Skills를 선택하는가(또는 관련 없는 것을 피하는가)? +- **준수:** 에이전트가 사용 전에 `SKILL.md`를 읽고 필요한 단계/인수를 따르는가? +- **워크플로 계약:** 도구 순서, 세션 기록 이어받기, 샌드박스 경계를 검증하는 다중 턴 시나리오. -향후 evals는 먼저 결정적으로 유지해야 합니다. +향후 평가는 우선 결정적으로 유지해야 합니다. -- 도구 호출 + 순서, skill 파일 읽기, 세션 배선을 검증하기 위해 mock provider를 사용하는 시나리오 러너. -- skill 중심 시나리오의 작은 제품군(사용 vs 회피, 게이팅, 프롬프트 인젝션). -- CI 안전 제품군이 마련된 뒤에만 선택 사항인 라이브 evals(옵트인, env 게이트). +- mock 제공자를 사용해 도구 호출 + 순서, Skills 파일 읽기, 세션 배선을 검증하는 시나리오 러너. +- Skills 중심 시나리오의 작은 스위트(사용 대 회피, 게이팅, 프롬프트 인젝션). +- CI 안전 스위트가 마련된 뒤에만 선택적 라이브 평가(옵트인, 환경 변수 게이트). -## 계약 테스트(plugin 및 채널 형태) +## 계약 테스트(Plugin 및 채널 형태) -계약 테스트는 등록된 모든 plugin과 채널이 해당 인터페이스 계약을 준수하는지 검증합니다. -발견된 모든 plugin을 순회하며 형태와 동작 assertion 제품군을 실행합니다. 기본 `pnpm test` 유닛 -레인은 이러한 공유 seam 및 스모크 파일을 의도적으로 건너뜁니다. 공유 채널 또는 provider surface를 -수정할 때는 계약 명령을 명시적으로 실행하세요. +계약 테스트는 등록된 모든 Plugin과 채널이 해당 인터페이스 계약을 준수하는지 검증합니다. +발견된 모든 Plugin을 순회하고 형태 및 동작 검증 스위트를 실행합니다. +기본 `pnpm test` 단위 레인은 이러한 공유 이음부 및 스모크 파일을 의도적으로 건너뜁니다. +공유 채널 또는 제공자 표면을 건드릴 때는 계약 명령을 명시적으로 실행하세요. ### 명령 - 모든 계약: `pnpm test:contracts` - 채널 계약만: `pnpm test:contracts:channels` -- Provider 계약만: `pnpm test:contracts:plugins` +- 제공자 계약만: `pnpm test:contracts:plugins` ### 채널 계약 -`src/channels/plugins/contracts/*.contract.test.ts`에 위치: +`src/channels/plugins/contracts/*.contract.test.ts`에 위치합니다. -- **plugin** - 기본 plugin 형태(id, 이름, capabilities) +- **plugin** - 기본 Plugin 형태(id, 이름, 기능) - **setup** - 설정 마법사 계약 - **session-binding** - 세션 바인딩 동작 - **outbound-payload** - 메시지 페이로드 구조 - **inbound** - 인바운드 메시지 처리 -- **actions** - 채널 action handler +- **actions** - 채널 작업 핸들러 - **threading** - 스레드 ID 처리 -- **directory** - 디렉터리/roster API -- **group-policy** - 그룹 정책 적용 +- **directory** - 디렉터리/명단 API +- **group-policy** - 그룹 정책 강제 -### Provider 상태 계약 +### 제공자 상태 계약 `src/plugins/contracts/*.contract.test.ts`에 위치합니다. - **status** - 채널 상태 프로브 -- **registry** - Plugin registry 형태 +- **registry** - Plugin 레지스트리 형태 -### Provider 계약 +### 제공자 계약 -`src/plugins/contracts/*.contract.test.ts`에 위치: +`src/plugins/contracts/*.contract.test.ts`에 위치합니다. -- **auth** - 인증 흐름 계약 +- **auth** - 인증 플로 계약 - **auth-choice** - 인증 선택/선정 - **catalog** - 모델 카탈로그 API -- **discovery** - Plugin discovery -- **loader** - Plugin loading -- **runtime** - Provider runtime -- **shape** - Plugin shape/interface +- **discovery** - Plugin 발견 +- **loader** - Plugin 로딩 +- **runtime** - 제공자 런타임 +- **shape** - Plugin 형태/인터페이스 - **wizard** - 설정 마법사 ### 실행 시점 -- plugin-sdk export 또는 subpath를 변경한 뒤 -- 채널 또는 provider plugin을 추가하거나 수정한 뒤 -- Plugin 등록 또는 discovery를 리팩터링한 뒤 +- plugin-sdk 내보내기 또는 하위 경로를 변경한 후 +- 채널 또는 제공자 Plugin을 추가하거나 수정한 후 +- Plugin 등록 또는 발견을 리팩터링한 후 계약 테스트는 CI에서 실행되며 실제 API 키가 필요하지 않습니다. -## 회귀 추가(가이드) +## 회귀 추가(지침) -라이브에서 발견한 provider/모델 문제를 수정할 때: +라이브에서 발견된 제공자/모델 문제를 수정할 때: -- 가능하면 CI 안전 회귀를 추가하세요(mock/stub provider, 또는 정확한 요청 형태 변환 캡처) -- 본질적으로 라이브 전용인 경우(rate limits, auth policies), 라이브 테스트를 좁게 유지하고 env vars를 통해 옵트인으로 만드세요 +- 가능하면 CI 안전 회귀를 추가하세요(mock/stub 제공자 또는 정확한 요청 형태 변환 캡처). +- 본질적으로 라이브 전용인 경우(레이트 리밋, 인증 정책)는 라이브 테스트를 좁게 유지하고 환경 변수를 통해 옵트인하세요. - 버그를 잡는 가장 작은 계층을 대상으로 하는 것을 선호하세요. - - provider 요청 변환/재생 버그 → 직접 models 테스트 + - 제공자 요청 변환/재생 버그 → 직접 모델 테스트 - Gateway 세션/기록/도구 파이프라인 버그 → Gateway 라이브 스모크 또는 CI 안전 Gateway mock 테스트 -- SecretRef traversal guardrail: - - `src/secrets/exec-secret-ref-id-parity.test.ts`는 registry metadata(`listSecretTargetRegistryEntries()`)에서 SecretRef 클래스당 하나의 샘플 target을 도출한 뒤, traversal-segment exec ids가 거부되는지 assertion합니다. - - `src/secrets/target-registry-data.ts`에 새로운 `includeInPlan` SecretRef target family를 추가하면 해당 테스트의 `classifyTargetClass`를 업데이트하세요. 이 테스트는 분류되지 않은 target ids에서 의도적으로 실패하므로 새 클래스를 조용히 건너뛸 수 없습니다. +- SecretRef 순회 가드레일: + - `src/secrets/exec-secret-ref-id-parity.test.ts`는 레지스트리 메타데이터(`listSecretTargetRegistryEntries()`)에서 SecretRef 클래스별 샘플 대상 하나를 도출한 다음, 순회 세그먼트 exec id가 거부되는지 검증합니다. + - `src/secrets/target-registry-data.ts`에 새 `includeInPlan` SecretRef 대상 패밀리를 추가하는 경우 해당 테스트의 `classifyTargetClass`를 업데이트하세요. 이 테스트는 분류되지 않은 대상 id에서 의도적으로 실패하므로 새 클래스를 조용히 건너뛸 수 없습니다. ## 관련 -- [Testing live](/ko/help/testing-live) -- [Testing updates and plugins](/ko/help/testing-updates-plugins) +- [라이브 테스트](/ko/help/testing-live) +- [업데이트 및 Plugin 테스트](/ko/help/testing-updates-plugins) - [CI](/ko/ci) diff --git a/docs/ko/plugins/google-meet.md b/docs/ko/plugins/google-meet.md index 5e5c7a053..2d9c2f85e 100644 --- a/docs/ko/plugins/google-meet.md +++ b/docs/ko/plugins/google-meet.md @@ -1,36 +1,36 @@ --- read_when: - - OpenClaw 에이전트가 Google Meet 회의에 참여하도록 하고 싶습니다 - - OpenClaw 에이전트가 새 Google Meet 통화를 만들도록 하고 싶습니다 + - OpenClaw 에이전트를 Google Meet 통화에 참여시키려는 경우 + - OpenClaw 에이전트가 새 Google Meet 통화를 만들도록 하려는 경우 - Google Meet 전송 수단으로 Chrome, Chrome 노드 또는 Twilio를 구성하고 있습니다 -summary: 'Google Meet Plugin: 명시적 Meet URL에 Chrome 또는 Twilio를 통해 참여하고 에이전트 응답 기본값 사용' +summary: 'Google Meet Plugin: Chrome 또는 Twilio를 통해 명시적인 Meet URL에 참가하고 에이전트 응답 기본값 사용' title: Google Meet Plugin x-i18n: - generated_at: "2026-05-04T06:24:24Z" + generated_at: "2026-05-04T07:03:03Z" model: gpt-5.5 provider: openai - source_hash: 459802231a807001d96d43950993f612234a5394fbe8c57a9992e97e8851dda2 + source_hash: 4268ad895bbf83d649b9571c0888c27eb982ad9710dfb408f22f7818cdc5dbcb source_path: plugins/google-meet.md workflow: 16 --- -OpenClaw의 Google Meet 참가자 지원은 설계상 명시적으로 동작합니다. +OpenClaw의 Google Meet 참가자 지원 — 이 Plugin은 의도적으로 명시적입니다: -- 명시적인 `https://meet.google.com/...` URL에만 참여합니다. -- Google Meet API를 통해 새 Meet 공간을 만든 다음 반환된 URL에 참여할 수 있습니다. -- `agent`는 기본 말하기 응답 모드입니다. 실시간 전사가 듣고, 구성된 OpenClaw 에이전트가 응답하며, 일반 OpenClaw TTS가 Meet에서 음성을 출력합니다. -- `bidi`는 대체용 직접 실시간 음성 모델 모드로 계속 사용할 수 있습니다. -- 에이전트는 `mode`로 참여 동작을 선택합니다. 실시간 듣기/말하기 응답에는 `agent`, 직접 실시간 음성 대체에는 `bidi`, 말하기 응답 브리지 없이 브라우저에 참여하고 제어하려면 `transcribe`를 사용합니다. +- 명시적인 `https://meet.google.com/...` URL에만 참가합니다. +- Google Meet API를 통해 새 Meet 공간을 만든 다음 반환된 URL에 참가할 수 있습니다. +- `agent`는 기본 응답 모드입니다. 실시간 전사가 듣고, 구성된 OpenClaw 에이전트가 답변하며, 일반 OpenClaw TTS가 Meet에서 말합니다. +- `bidi`는 예비 직접 실시간 음성 모델 모드로 계속 사용할 수 있습니다. +- 에이전트는 `mode`로 참가 동작을 선택합니다. 실시간 듣기/응답에는 `agent`, 직접 실시간 음성 예비 경로에는 `bidi`, 응답 브리지 없이 브라우저 참가/제어에는 `transcribe`를 사용합니다. - 인증은 개인 Google OAuth 또는 이미 로그인된 Chrome 프로필로 시작합니다. -- 자동 동의 안내 방송은 없습니다. +- 자동 동의 안내는 없습니다. - 기본 Chrome 오디오 백엔드는 `BlackHole 2ch`입니다. - Chrome은 로컬 또는 페어링된 노드 호스트에서 실행할 수 있습니다. - Twilio는 전화 접속 번호와 선택적 PIN 또는 DTMF 시퀀스를 받습니다. Meet URL로 직접 전화를 걸 수는 없습니다. -- CLI 명령은 `googlemeet`입니다. `meet`는 더 넓은 에이전트 원격 회의 워크플로용으로 예약되어 있습니다. +- CLI 명령은 `googlemeet`입니다. `meet`는 더 광범위한 에이전트 원격 회의 워크플로용으로 예약되어 있습니다. ## 빠른 시작 -로컬 오디오 의존성을 설치하고 실시간 전사 제공자와 일반 OpenClaw TTS를 구성합니다. OpenAI가 기본 전사 제공자입니다. Google Gemini Live도 `realtime.voiceProvider: "google"`이 설정된 별도의 `bidi` 음성 대체로 사용할 수 있습니다. +로컬 오디오 의존성을 설치하고 실시간 전사 제공자와 일반 OpenClaw TTS를 구성합니다. OpenAI가 기본 전사 제공자입니다. Google Gemini Live도 별도의 `bidi` 음성 예비 경로로 작동하며, `realtime.voiceProvider: "google"`을 사용합니다. ```bash brew install blackhole-2ch sox @@ -39,7 +39,7 @@ export OPENAI_API_KEY=sk-... export GEMINI_API_KEY=... ``` -`blackhole-2ch`는 `BlackHole 2ch` 가상 오디오 장치를 설치합니다. Homebrew의 설치 프로그램은 macOS가 장치를 노출하기 전에 재부팅을 요구합니다. +`blackhole-2ch`는 `BlackHole 2ch` 가상 오디오 장치를 설치합니다. Homebrew 설치 프로그램은 macOS가 장치를 노출하기 전에 재부팅이 필요합니다. ```bash sudo reboot @@ -73,13 +73,13 @@ Plugin을 활성화합니다. openclaw googlemeet setup ``` -설정 출력은 에이전트가 읽을 수 있고 모드를 인식하도록 설계되었습니다. Chrome 프로필, 노드 고정, 그리고 실시간 Chrome 참여의 경우 BlackHole/SoX 오디오 브리지와 지연된 실시간 인트로 검사를 보고합니다. 관찰 전용 참여의 경우 `--mode transcribe`로 동일한 전송을 확인합니다. 이 모드는 브리지를 통해 듣거나 말하지 않기 때문에 실시간 오디오 전제 조건을 건너뜁니다. +설정 출력은 에이전트가 읽을 수 있고 모드를 인식하도록 설계되어 있습니다. Chrome 프로필, 노드 고정, 그리고 실시간 Chrome 참가의 경우 BlackHole/SoX 오디오 브리지와 지연된 실시간 인트로 확인을 보고합니다. 관찰 전용 참가의 경우 `--mode transcribe`로 동일한 전송을 확인합니다. 해당 모드는 브리지를 통해 듣거나 말하지 않으므로 실시간 오디오 필수 조건을 건너뜁니다. ```bash openclaw googlemeet setup --transport chrome-node --mode transcribe ``` -Twilio 위임이 구성된 경우 설정은 `voice-call` Plugin, Twilio 자격 증명, 공개 Webhook 노출이 준비되었는지도 보고합니다. 에이전트에게 참여를 요청하기 전에 `ok: false` 검사는 해당 전송 및 모드의 차단 요소로 취급합니다. 스크립트 또는 기계 판독 가능한 출력에는 `openclaw googlemeet setup --json`을 사용합니다. 에이전트가 시도하기 전에 특정 전송을 사전 점검하려면 `--transport chrome`, `--transport chrome-node`, 또는 `--transport twilio`를 사용합니다. +Twilio 위임이 구성되어 있으면 설정은 `voice-call` Plugin, Twilio 자격 증명, 공개 Webhook 노출이 준비되었는지도 보고합니다. 에이전트에게 참가를 요청하기 전에 모든 `ok: false` 확인을 확인된 전송 및 모드의 차단 요소로 처리합니다. 스크립트 또는 기계가 읽을 수 있는 출력에는 `openclaw googlemeet setup --json`을 사용합니다. 에이전트가 시도하기 전에 특정 전송을 사전 점검하려면 `--transport chrome`, `--transport chrome-node`, 또는 `--transport twilio`를 사용합니다. Twilio의 경우 기본 전송이 Chrome이면 항상 전송을 명시적으로 사전 점검합니다. @@ -87,15 +87,15 @@ Twilio의 경우 기본 전송이 Chrome이면 항상 전송을 명시적으로 openclaw googlemeet setup --transport twilio ``` -이렇게 하면 에이전트가 회의에 전화를 걸기 전에 누락된 `voice-call` 연결, Twilio 자격 증명 또는 도달할 수 없는 Webhook 노출을 잡아냅니다. +이렇게 하면 에이전트가 회의에 전화하기 전에 누락된 `voice-call` 배선, Twilio 자격 증명, 또는 도달할 수 없는 Webhook 노출을 포착합니다. -회의에 참여합니다. +회의에 참가합니다. ```bash openclaw googlemeet join https://meet.google.com/abc-defg-hij ``` -또는 에이전트가 `google_meet` 도구를 통해 참여하게 합니다. +또는 에이전트가 `google_meet` 도구를 통해 참가하게 합니다. ```json { @@ -106,25 +106,25 @@ openclaw googlemeet join https://meet.google.com/abc-defg-hij } ``` -에이전트용 `google_meet` 도구는 macOS가 아닌 호스트에서도 아티팩트, 캘린더, 설정, 전사, Twilio, `chrome-node` 흐름에 계속 사용할 수 있습니다. 로컬 Chrome 말하기 응답 작업은 번들 Chrome 오디오 경로가 현재 macOS `BlackHole 2ch`에 의존하기 때문에 해당 호스트에서 차단됩니다. Linux에서는 Chrome 말하기 응답 참가에 `mode: "transcribe"`, Twilio 전화 접속, 또는 macOS `chrome-node` 호스트를 사용합니다. +에이전트용 `google_meet` 도구는 macOS가 아닌 호스트에서도 아티팩트, 캘린더, 설정, 전사, Twilio, `chrome-node` 흐름에 계속 사용할 수 있습니다. 로컬 Chrome 응답 작업은 번들 Chrome 오디오 경로가 현재 macOS `BlackHole 2ch`에 의존하므로 그곳에서는 차단됩니다. Linux에서는 Chrome 응답 참여에 `mode: "transcribe"`, Twilio 전화 접속, 또는 macOS `chrome-node` 호스트를 사용합니다. -새 회의를 만들고 참여합니다. +새 회의를 만들고 참가합니다. ```bash openclaw googlemeet create --transport chrome-node --mode agent ``` -API로 만든 방의 경우, 방의 노크 없는 참여 정책을 Google 계정 기본값에서 상속하지 않고 명시하려면 Google Meet `SpaceConfig.accessType`을 사용합니다. +API로 만든 방에서 방의 노크 없는 정책을 Google 계정 기본값에서 상속하지 않고 명시적으로 지정하려면 Google Meet `SpaceConfig.accessType`을 사용합니다. ```bash openclaw googlemeet create --access-type OPEN --transport chrome-node --mode agent ``` -`OPEN`은 Meet URL이 있는 누구나 노크 없이 참여할 수 있게 합니다. `TRUSTED`는 호스트 조직의 신뢰된 사용자, 초대된 외부 사용자, 전화 접속 사용자가 노크 없이 참여할 수 있게 합니다. `RESTRICTED`는 노크 없는 입장을 초대받은 사람으로 제한합니다. 이러한 설정은 공식 Google Meet API 생성 경로에만 적용되므로 OAuth 자격 증명이 구성되어 있어야 합니다. +`OPEN`은 Meet URL이 있는 누구나 노크 없이 참가할 수 있게 합니다. `TRUSTED`는 호스트 조직의 신뢰할 수 있는 사용자, 초대된 외부 사용자, 전화 접속 사용자가 노크 없이 참가할 수 있게 합니다. `RESTRICTED`는 노크 없는 입장을 초대받은 사람으로 제한합니다. 이 설정은 공식 Google Meet API 생성 경로에만 적용되므로 OAuth 자격 증명을 구성해야 합니다. -이 옵션을 사용할 수 있기 전에 Google Meet 인증을 완료했다면 Google OAuth 동의 화면에 `meetings.space.settings` 범위를 추가한 후 `openclaw googlemeet auth login --json`을 다시 실행합니다. +이 옵션이 제공되기 전에 Google Meet 인증을 완료했다면 Google OAuth 동의 화면에 `meetings.space.settings` 범위를 추가한 후 `openclaw googlemeet auth login --json`을 다시 실행합니다. -참여하지 않고 URL만 만듭니다. +참가하지 않고 URL만 만듭니다. ```bash openclaw googlemeet create --no-join @@ -133,12 +133,12 @@ openclaw googlemeet create --no-join `googlemeet create`에는 두 가지 경로가 있습니다. - API 생성: Google Meet OAuth 자격 증명이 구성된 경우 사용됩니다. 가장 결정적인 경로이며 브라우저 UI 상태에 의존하지 않습니다. -- 브라우저 대체: OAuth 자격 증명이 없을 때 사용됩니다. OpenClaw는 고정된 Chrome 노드를 사용해 `https://meet.google.com/new`를 열고, Google이 실제 회의 코드 URL로 리디렉션할 때까지 기다린 다음 해당 URL을 반환합니다. 이 경로에서는 노드의 OpenClaw Chrome 프로필이 이미 Google에 로그인되어 있어야 합니다. 브라우저 자동화는 Meet 자체의 최초 실행 마이크 프롬프트를 처리합니다. 해당 프롬프트는 Google 로그인 실패로 취급되지 않습니다. - 참여 및 생성 흐름은 새 탭을 열기 전에 기존 Meet 탭도 재사용하려고 합니다. 매칭은 `authuser` 같은 무해한 URL 쿼리 문자열을 무시하므로, 에이전트가 재시도하면 두 번째 Chrome 탭을 만들지 않고 이미 열린 회의에 포커스해야 합니다. +- 브라우저 예비 경로: OAuth 자격 증명이 없는 경우 사용됩니다. OpenClaw는 고정된 Chrome 노드를 사용해 `https://meet.google.com/new`를 열고, Google이 실제 회의 코드 URL로 리디렉션할 때까지 기다린 다음 해당 URL을 반환합니다. 이 경로는 노드의 OpenClaw Chrome 프로필이 이미 Google에 로그인되어 있어야 합니다. 브라우저 자동화는 Meet 자체의 최초 실행 마이크 프롬프트를 처리합니다. 해당 프롬프트는 Google 로그인 실패로 간주되지 않습니다. + 참가 및 생성 흐름은 새 탭을 열기 전에 기존 Meet 탭도 재사용하려고 시도합니다. 일치는 `authuser` 같은 무해한 URL 쿼리 문자열을 무시하므로, 에이전트 재시도는 두 번째 Chrome 탭을 만드는 대신 이미 열린 회의에 포커스해야 합니다. -명령/도구 출력에는 에이전트가 어떤 경로가 사용되었는지 설명할 수 있도록 `source` 필드(`api` 또는 `browser`)가 포함됩니다. `create`는 기본적으로 새 회의에 참여하고 `joined: true`와 참여 세션을 반환합니다. URL만 만들려면 CLI에서 `create --no-join`을 사용하거나 도구에 `"join": false`를 전달합니다. +명령/도구 출력에는 사용된 경로를 에이전트가 설명할 수 있도록 `source` 필드(`api` 또는 `browser`)가 포함됩니다. `create`는 기본적으로 새 회의에 참가하며 `joined: true`와 참가 세션을 반환합니다. URL만 발급하려면 CLI에서 `create --no-join`을 사용하거나 도구에 `"join": false`를 전달합니다. -또는 에이전트에게 "Google Meet을 만들고, 에이전트 말하기 응답 모드로 참여한 다음, 링크를 보내줘."라고 말합니다. 에이전트는 `action: "create"`로 `google_meet`를 호출한 다음 반환된 `meetingUri`를 공유해야 합니다. +또는 에이전트에게 이렇게 말합니다. "Google Meet을 만들고, 에이전트 응답 모드로 참가한 다음, 링크를 보내줘." 에이전트는 `action: "create"`로 `google_meet`를 호출한 다음 반환된 `meetingUri`를 공유해야 합니다. ```json { @@ -148,22 +148,22 @@ openclaw googlemeet create --no-join } ``` -관찰 전용/브라우저 제어 참여의 경우 `"mode": "transcribe"`를 설정합니다. 이 모드는 양방향 실시간 음성 브리지를 시작하지 않고, BlackHole 또는 SoX가 필요 없으며, 회의에 말하기 응답을 하지 않습니다. 이 모드의 Chrome 참여는 OpenClaw의 마이크/카메라 권한 부여와 Meet **Use microphone** 경로도 피합니다. Meet이 오디오 선택 중간 화면을 표시하면 자동화는 마이크 없는 경로를 시도하고, 그렇지 않으면 로컬 마이크를 열지 않고 수동 작업을 보고합니다. 전사 모드에서 관리형 Chrome 전송은 최선 노력 방식의 Meet 캡션 관찰자도 설치합니다. `googlemeet status --json` 및 `googlemeet doctor`는 운영자가 브라우저가 통화에 참여했는지, Meet 캡션이 텍스트를 생성하는지 알 수 있도록 `captioning`, `captionsEnabledAttempted`, `transcriptLines`, `lastCaptionAt`, `lastCaptionSpeaker`, `lastCaptionText`, 짧은 `recentTranscript` 꼬리를 노출합니다. -예/아니요 프로브가 필요하면 `openclaw googlemeet test-listen --transport chrome-node`를 사용합니다. 이 명령은 전사 모드로 참여하고, 새 캡션 또는 전사 움직임을 기다린 다음 `listenVerified`, `listenTimedOut`, 수동 작업 필드, 최신 캡션 상태를 반환합니다. +관찰 전용/브라우저 제어 참가에는 `"mode": "transcribe"`를 설정합니다. 이는 양방향 실시간 음성 브리지를 시작하지 않고, BlackHole이나 SoX가 필요하지 않으며, 회의에서 응답하지 않습니다. 이 모드의 Chrome 참가는 OpenClaw의 마이크/카메라 권한 부여와 Meet **마이크 사용** 경로도 피합니다. Meet이 오디오 선택 중간 화면을 표시하면 자동화는 마이크 없는 경로를 시도하고, 그렇지 않으면 로컬 마이크를 여는 대신 수동 작업을 보고합니다. 전사 모드에서 관리형 Chrome 전송은 최선의 Meet 자막 관찰자도 설치합니다. `googlemeet status --json`과 `googlemeet doctor`는 `captioning`, `captionsEnabledAttempted`, `transcriptLines`, `lastCaptionAt`, `lastCaptionSpeaker`, `lastCaptionText`, 그리고 짧은 `recentTranscript` 꼬리를 표시하여 운영자가 브라우저가 통화에 참가했는지와 Meet 자막이 텍스트를 생성하는지 확인할 수 있게 합니다. +예/아니요 탐지가 필요할 때는 `openclaw googlemeet test-listen --transport chrome-node`를 사용합니다. 이 명령은 전사 모드로 참가하고, 새로운 자막 또는 전사 움직임을 기다린 다음, `listenVerified`, `listenTimedOut`, 수동 작업 필드, 최신 자막 상태를 반환합니다. -실시간 세션 중 `google_meet` 상태에는 `inCall`, `manualActionRequired`, `providerConnected`, `realtimeReady`, `audioInputActive`, `audioOutputActive`, 마지막 입력/출력 타임스탬프, 바이트 카운터, 브리지 종료 상태 같은 브라우저 및 오디오 브리지 상태가 포함됩니다. 안전한 Meet 페이지 프롬프트가 나타나면 브라우저 자동화가 가능한 경우 이를 처리합니다. 로그인, 호스트 승인, 브라우저/OS 권한 프롬프트는 에이전트가 전달할 수 있도록 이유와 메시지를 포함한 수동 작업으로 보고됩니다. 관리형 Chrome 세션은 브라우저 상태가 `inCall: true`를 보고한 후에만 인트로 또는 테스트 문구를 내보냅니다. 그렇지 않으면 상태가 `speechReady: false`를 보고하고, 에이전트가 실제로 회의에서 말한 것처럼 가장하지 않고 말하기 시도를 차단합니다. +실시간 세션 중 `google_meet` 상태에는 `inCall`, `manualActionRequired`, `providerConnected`, `realtimeReady`, `audioInputActive`, `audioOutputActive`, 마지막 입력/출력 타임스탬프, 바이트 카운터, 브리지 닫힘 상태 같은 브라우저 및 오디오 브리지 상태가 포함됩니다. 안전한 Meet 페이지 프롬프트가 나타나면 브라우저 자동화가 가능한 경우 이를 처리합니다. 로그인, 호스트 승인, 브라우저/OS 권한 프롬프트는 에이전트가 전달할 이유와 메시지가 포함된 수동 작업으로 보고됩니다. 관리형 Chrome 세션은 브라우저 상태가 `inCall: true`를 보고한 후에만 인트로 또는 테스트 문구를 내보냅니다. 그렇지 않으면 상태는 `speechReady: false`를 보고하고, 에이전트가 회의에서 말했다고 가장하는 대신 음성 시도가 차단됩니다. -로컬 Chrome은 로그인된 OpenClaw 브라우저 프로필을 통해 참여합니다. 실시간 모드에는 OpenClaw가 사용하는 마이크/스피커 경로를 위해 `BlackHole 2ch`가 필요합니다. 깨끗한 양방향 오디오를 위해 별도의 가상 장치 또는 Loopback 스타일 그래프를 사용합니다. 단일 BlackHole 장치만으로도 첫 스모크 테스트에는 충분하지만 에코가 발생할 수 있습니다. +로컬 Chrome 참가는 로그인된 OpenClaw 브라우저 프로필을 통해 수행됩니다. 실시간 모드는 OpenClaw가 사용하는 마이크/스피커 경로에 `BlackHole 2ch`가 필요합니다. 깨끗한 양방향 오디오를 위해 별도의 가상 장치 또는 Loopback 스타일 그래프를 사용합니다. 단일 BlackHole 장치는 첫 스모크 테스트에는 충분하지만 에코가 발생할 수 있습니다. ### 로컬 Gateway + Parallels Chrome -VM이 Chrome을 소유하게 만들기 위해 macOS VM 안에 전체 OpenClaw Gateway나 모델 API 키가 필요하지는 않습니다. Gateway와 에이전트를 로컬에서 실행한 다음 VM에서 노드 호스트를 실행합니다. 노드가 Chrome 명령을 광고하도록 VM에서 번들 Plugin을 한 번 활성화합니다. +VM이 Chrome을 소유하게 하는 것만을 위해 macOS VM 안에 전체 OpenClaw Gateway 또는 모델 API 키가 필요하지는 않습니다. Gateway와 에이전트를 로컬에서 실행한 다음 VM에서 노드 호스트를 실행합니다. VM에서 번들 Plugin을 한 번 활성화하여 노드가 Chrome 명령을 광고하게 합니다. -각 위치에서 실행되는 항목: +실행 위치: - Gateway 호스트: OpenClaw Gateway, 에이전트 워크스페이스, 모델/API 키, 실시간 제공자, Google Meet Plugin 구성. - Parallels macOS VM: OpenClaw CLI/노드 호스트, Google Chrome, SoX, BlackHole 2ch, Google에 로그인된 Chrome 프로필. -- VM에 필요 없는 항목: Gateway 서비스, 에이전트 구성, OpenAI/GPT 키, 모델 제공자 설정. +- VM에 필요하지 않은 것: Gateway 서비스, 에이전트 구성, OpenAI/GPT 키, 또는 모델 제공자 설정. VM 의존성을 설치합니다. @@ -171,7 +171,7 @@ VM 의존성을 설치합니다. brew install blackhole-2ch sox ``` -BlackHole을 설치한 후 macOS가 `BlackHole 2ch`를 노출하도록 VM을 재부팅합니다. +BlackHole 설치 후 macOS가 `BlackHole 2ch`를 노출하도록 VM을 재부팅합니다. ```bash sudo reboot @@ -184,7 +184,7 @@ system_profiler SPAudioDataType | grep -i BlackHole command -v sox ``` -VM에 OpenClaw를 설치하거나 업데이트한 다음, 거기에서 번들 Plugin을 활성화합니다. +VM에서 OpenClaw를 설치하거나 업데이트한 다음, 그곳에서 번들 Plugin을 활성화합니다. ```bash openclaw plugins enable google-meet @@ -196,7 +196,7 @@ VM에서 노드 호스트를 시작합니다. openclaw node run --host --port 18789 --display-name parallels-macos ``` -``가 LAN IP이고 TLS를 사용하지 않는 경우, 신뢰된 개인 네트워크에 명시적으로 동의하지 않으면 노드가 일반 텍스트 WebSocket을 거부합니다. +``가 LAN IP이고 TLS를 사용하지 않는 경우, 신뢰할 수 있는 해당 사설 네트워크를 명시적으로 허용하지 않으면 노드는 평문 WebSocket을 거부합니다. ```bash OPENCLAW_ALLOW_INSECURE_PRIVATE_WS=1 \ @@ -211,7 +211,7 @@ OPENCLAW_ALLOW_INSECURE_PRIVATE_WS=1 \ openclaw node restart ``` -`OPENCLAW_ALLOW_INSECURE_PRIVATE_WS=1`은 프로세스 환경이며 `openclaw.json` 설정이 아닙니다. `openclaw node install`은 설치 명령에 이 값이 있을 때 LaunchAgent 환경에 저장합니다. +`OPENCLAW_ALLOW_INSECURE_PRIVATE_WS=1`은 프로세스 환경이며 `openclaw.json` 설정이 아닙니다. `openclaw node install`은 설치 명령에 이 값이 있으면 LaunchAgent 환경에 저장합니다. Gateway 호스트에서 노드를 승인합니다. @@ -220,13 +220,13 @@ openclaw devices list openclaw devices approve ``` -Gateway가 노드를 보고 있고 노드가 `googlemeet.chrome` 및 브라우저 기능/`browser.proxy`를 모두 광고하는지 확인합니다. +Gateway가 노드를 보고 있으며, 노드가 `googlemeet.chrome`와 브라우저 기능/`browser.proxy`를 모두 광고하는지 확인합니다. ```bash openclaw nodes status ``` -Gateway 호스트에서 해당 노드를 통해 Meet을 라우팅합니다. +Gateway 호스트에서 Meet을 해당 노드로 라우팅합니다. ```json5 { @@ -256,64 +256,63 @@ Gateway 호스트에서 해당 노드를 통해 Meet을 라우팅합니다. } ``` -이제 Gateway 호스트에서 일반적으로 참여합니다. +이제 Gateway 호스트에서 일반적으로 참가합니다. ```bash openclaw googlemeet join https://meet.google.com/abc-defg-hij ``` -또는 에이전트에게 `transport: "chrome-node"`로 `google_meet` 도구를 사용하도록 요청합니다. +또는 에이전트에게 `transport: "chrome-node"`로 `google_meet` 도구를 사용하라고 요청합니다. -세션을 만들거나 재사용하고, 알려진 문구를 말한 뒤, 세션 상태를 출력하는 단일 명령 스모크 테스트: +세션을 만들거나 재사용하고, 알려진 문구를 말한 다음, 세션 상태를 출력하는 단일 명령 스모크 테스트: ```bash openclaw googlemeet test-speech https://meet.google.com/abc-defg-hij ``` 실시간 참여 중에는 OpenClaw 브라우저 자동화가 게스트 이름을 입력하고, -참여/참여 요청을 클릭하며, 해당 프롬프트가 표시되면 Meet의 첫 실행 -"마이크 사용" 선택을 수락합니다. 관찰 전용 참여 또는 브라우저 전용 회의 생성 -중에는 해당 선택을 사용할 수 있을 때 마이크 없이 동일한 프롬프트를 지나갑니다. -브라우저 프로필이 로그인되어 있지 않거나, Meet이 호스트 승인을 기다리고 있거나, -실시간 참여를 위해 Chrome에 마이크/카메라 권한이 필요하거나, Meet이 자동화로 -해결할 수 없는 프롬프트에 멈춰 있는 경우, 참여/test-speech 결과는 -`manualActionRequired: true`를 `manualActionReason` 및 -`manualActionMessage`와 함께 보고합니다. 에이전트는 참여 재시도를 중지하고, -해당 정확한 메시지와 현재 `browserUrl`/`browserTitle`을 보고한 뒤, 수동 -브라우저 작업이 완료된 후에만 다시 시도해야 합니다. +Join/Ask to join을 클릭하며, 해당 프롬프트가 나타나면 Meet의 첫 실행 +"Use microphone" 선택을 수락합니다. 관찰 전용 참여 또는 브라우저 전용 회의 +생성 중에는 해당 선택지를 사용할 수 있을 때 마이크 없이 같은 프롬프트를 +지나갑니다. 브라우저 프로필이 로그인되어 있지 않거나, Meet이 호스트 승인을 +기다리고 있거나, Chrome에 실시간 참여를 위한 마이크/카메라 권한이 필요하거나, +Meet이 자동화로 해결할 수 없는 프롬프트에서 멈춘 경우 참여/test-speech 결과는 +`manualActionReason` 및 `manualActionMessage`와 함께 +`manualActionRequired: true`를 보고합니다. 에이전트는 참여 재시도를 중지하고, +그 정확한 메시지와 현재 `browserUrl`/`browserTitle`을 보고한 뒤, 수동 브라우저 +작업이 완료된 후에만 재시도해야 합니다. -`chromeNode.node`가 생략되면, OpenClaw는 정확히 하나의 연결된 노드만 -`googlemeet.chrome`과 브라우저 제어를 모두 알리는 경우에만 자동 선택합니다. -여러 사용 가능한 노드가 연결되어 있으면 `chromeNode.node`를 노드 ID, -표시 이름 또는 원격 IP로 설정하세요. +`chromeNode.node`가 생략되면, 연결된 노드가 정확히 하나이고 그 노드가 +`googlemeet.chrome` 및 브라우저 제어를 모두 알릴 때만 OpenClaw가 자동 선택합니다. +기능을 갖춘 노드가 여러 개 연결되어 있으면 `chromeNode.node`를 노드 ID, 표시 이름 +또는 원격 IP로 설정하세요. -일반적인 실패 점검: +일반적인 실패 확인 사항: - `Configured Google Meet node ... is not usable: offline`: 고정된 노드는 - Gateway에 알려져 있지만 사용할 수 없습니다. 에이전트는 해당 노드를 - 사용 가능한 Chrome 호스트가 아니라 진단 상태로 취급해야 하며, 사용자가 - 요청하지 않은 한 다른 전송으로 대체하지 말고 설정 차단 요인을 보고해야 - 합니다. -- `No connected Google Meet-capable node`: VM에서 `openclaw node run`을 - 시작하고, 페어링을 승인하며, VM에서 `openclaw plugins enable google-meet`와 + Gateway에 알려져 있지만 사용할 수 없습니다. 에이전트는 해당 노드를 사용 가능한 + Chrome 호스트가 아니라 진단 상태로 취급해야 하며, 사용자가 요청하지 않는 한 + 다른 전송 방식으로 대체하지 말고 설정 차단 요인을 보고해야 합니다. +- `No connected Google Meet-capable node`: VM에서 `openclaw node run`을 시작하고, + 페어링을 승인한 뒤, VM에서 `openclaw plugins enable google-meet` 및 `openclaw plugins enable browser`가 실행되었는지 확인하세요. 또한 Gateway 호스트가 `gateway.nodes.allowCommands: ["googlemeet.chrome", "browser.proxy"]`로 - 두 노드 명령을 모두 허용하는지도 확인하세요. -- `BlackHole 2ch audio device not found`: 점검 중인 호스트에 - `blackhole-2ch`를 설치하고 로컬 Chrome 오디오를 사용하기 전에 재부팅하세요. + 두 노드 명령을 모두 허용하는지 확인하세요. +- `BlackHole 2ch audio device not found`: 확인 중인 호스트에 `blackhole-2ch`를 + 설치하고, 로컬 Chrome 오디오를 사용하기 전에 재부팅하세요. - `BlackHole 2ch audio device not found on the node`: VM에 `blackhole-2ch`를 설치하고 VM을 재부팅하세요. -- Chrome이 열리지만 참여할 수 없음: VM 내부의 브라우저 프로필에 로그인하거나, - 게스트 참여를 위해 `chrome.guestName`을 설정된 상태로 유지하세요. 게스트 - 자동 참여는 노드 브라우저 프록시를 통해 OpenClaw 브라우저 자동화를 - 사용합니다. 노드 브라우저 구성이 원하는 프로필을 가리키는지 확인하세요. - 예: `browser.defaultProfile: "user"` 또는 이름이 지정된 기존 세션 프로필. +- Chrome은 열리지만 참여할 수 없음: VM 내부의 브라우저 프로필에 로그인하거나, + 게스트 참여를 위해 `chrome.guestName`을 설정된 상태로 유지하세요. 게스트 자동 + 참여는 노드 브라우저 프록시를 통해 OpenClaw 브라우저 자동화를 사용합니다. 예를 + 들어 `browser.defaultProfile: "user"` 또는 이름이 지정된 기존 세션 프로필처럼 + 노드 브라우저 구성이 원하는 프로필을 가리키는지 확인하세요. - 중복 Meet 탭: `chrome.reuseExistingTab: true`를 활성화된 상태로 두세요. - OpenClaw는 새 탭을 열기 전에 동일한 Meet URL의 기존 탭을 활성화하고, - 브라우저 회의 생성은 다른 탭을 열기 전에 진행 중인 - `https://meet.google.com/new` 또는 Google 계정 프롬프트 탭을 재사용합니다. -- 오디오 없음: Meet에서 마이크/스피커를 OpenClaw가 사용하는 가상 오디오 - 장치 경로로 라우팅하세요. 깨끗한 양방향 오디오를 위해 별도의 가상 장치나 + OpenClaw는 새 탭을 열기 전에 같은 Meet URL의 기존 탭을 활성화하며, 브라우저 + 회의 생성은 다른 탭을 열기 전에 진행 중인 `https://meet.google.com/new` 또는 + Google 계정 프롬프트 탭을 재사용합니다. +- 오디오 없음: Meet에서 마이크/스피커를 OpenClaw가 사용하는 가상 오디오 장치 + 경로로 라우팅하세요. 깨끗한 양방향 오디오를 위해 별도의 가상 장치 또는 Loopback 스타일 라우팅을 사용하세요. ## 설치 참고 사항 @@ -321,47 +320,46 @@ openclaw googlemeet test-speech https://meet.google.com/abc-defg-hij Chrome 토크백 기본값은 두 가지 외부 도구를 사용합니다. - `sox`: 명령줄 오디오 유틸리티입니다. Plugin은 기본 24 kHz PCM16 오디오 - 브리지에 명시적 CoreAudio 장치 명령을 사용합니다. -- `blackhole-2ch`: macOS 가상 오디오 드라이버입니다. Chrome/Meet이 라우팅할 - 수 있는 `BlackHole 2ch` 오디오 장치를 만듭니다. + 브리지를 위해 명시적인 CoreAudio 장치 명령을 사용합니다. +- `blackhole-2ch`: macOS 가상 오디오 드라이버입니다. Chrome/Meet이 라우팅할 수 + 있는 `BlackHole 2ch` 오디오 장치를 생성합니다. -OpenClaw는 두 패키지 중 어느 것도 번들로 제공하거나 재배포하지 않습니다. -문서는 사용자에게 Homebrew를 통해 호스트 의존성으로 설치하도록 안내합니다. -SoX는 `LGPL-2.0-only AND GPL-2.0-only`로 라이선스가 부여되며, BlackHole은 -GPL-3.0입니다. BlackHole을 OpenClaw와 함께 번들로 제공하는 설치 프로그램이나 -어플라이언스를 빌드하는 경우, BlackHole의 업스트림 라이선스 조건을 검토하거나 -Existential Audio에서 별도 라이선스를 받으세요. +OpenClaw는 두 패키지 모두 번들로 제공하거나 재배포하지 않습니다. 문서는 +사용자에게 Homebrew를 통해 호스트 종속성으로 설치하라고 안내합니다. SoX의 +라이선스는 `LGPL-2.0-only AND GPL-2.0-only`이고, BlackHole은 GPL-3.0입니다. +BlackHole을 OpenClaw와 함께 번들로 제공하는 설치 관리자 또는 어플라이언스를 +빌드하는 경우, BlackHole의 업스트림 라이선스 조건을 검토하거나 Existential +Audio에서 별도 라이선스를 받으세요. -## 전송 +## 전송 방식 ### Chrome -Chrome 전송은 OpenClaw 브라우저 제어를 통해 Meet URL을 열고 로그인된 +Chrome 전송 방식은 OpenClaw 브라우저 제어를 통해 Meet URL을 열고, 로그인된 OpenClaw 브라우저 프로필로 참여합니다. macOS에서는 Plugin이 실행 전에 `BlackHole 2ch`를 확인합니다. 구성된 경우 Chrome을 열기 전에 오디오 브리지 -상태 명령과 시작 명령도 실행합니다. Chrome/오디오가 Gateway 호스트에 있으면 -`chrome`을 사용하고, Chrome/오디오가 Parallels macOS VM과 같은 페어링된 -노드에 있으면 `chrome-node`를 사용하세요. 로컬 Chrome의 경우 -`browser.defaultProfile`로 프로필을 선택하세요. `chrome.browserProfile`은 -`chrome-node` 호스트에 전달됩니다. +상태 명령과 시작 명령도 실행합니다. Chrome/오디오가 Gateway 호스트에 있을 때는 +`chrome`을 사용하고, Chrome/오디오가 Parallels macOS VM 같은 페어링된 노드에 +있을 때는 `chrome-node`를 사용하세요. 로컬 Chrome의 경우 `browser.defaultProfile`로 +프로필을 선택하세요. `chrome.browserProfile`은 `chrome-node` 호스트에 전달됩니다. ```bash openclaw googlemeet join https://meet.google.com/abc-defg-hij --transport chrome openclaw googlemeet join https://meet.google.com/abc-defg-hij --transport chrome-node ``` -Chrome 마이크와 스피커 오디오를 로컬 OpenClaw 오디오 브리지를 통해 -라우팅하세요. `BlackHole 2ch`가 설치되어 있지 않으면 오디오 경로 없이 -조용히 참여하는 대신 설정 오류로 참여가 실패합니다. +Chrome 마이크와 스피커 오디오를 로컬 OpenClaw 오디오 브리지로 라우팅하세요. +`BlackHole 2ch`가 설치되어 있지 않으면 오디오 경로 없이 조용히 참여하는 대신 +설정 오류와 함께 참여가 실패합니다. ### Twilio -Twilio 전송은 Voice Call Plugin에 위임되는 엄격한 다이얼 플랜입니다. +Twilio 전송 방식은 Voice Call Plugin에 위임되는 엄격한 전화 연결 계획입니다. Meet 페이지에서 전화번호를 파싱하지 않습니다. -Chrome 참여를 사용할 수 없거나 전화 다이얼인 대체 경로가 필요할 때 -사용하세요. Google Meet은 회의에 대한 전화 다이얼인 번호와 PIN을 노출해야 -합니다. OpenClaw는 Meet 페이지에서 이를 검색하지 않습니다. +Chrome 참여를 사용할 수 없거나 전화 다이얼인 대체 경로가 필요할 때 사용하세요. +Google Meet은 회의에 대한 전화 다이얼인 번호와 PIN을 노출해야 하며, OpenClaw는 +Meet 페이지에서 이를 검색하지 않습니다. Chrome 노드가 아니라 Gateway 호스트에서 Voice Call Plugin을 활성화하세요. @@ -404,8 +402,8 @@ Chrome 노드가 아니라 Gateway 호스트에서 Voice Call Plugin을 활성 } ``` -환경 또는 구성을 통해 Twilio 자격 증명을 제공하세요. 환경은 비밀 값을 -`openclaw.json` 밖에 유지합니다. +환경 또는 구성을 통해 Twilio 자격 증명을 제공하세요. 환경을 사용하면 비밀이 +`openclaw.json` 밖에 유지됩니다. ```bash export TWILIO_ACCOUNT_SID=AC... @@ -414,12 +412,12 @@ export TWILIO_FROM_NUMBER=+15550001234 export GEMINI_API_KEY=... ``` -실시간 음성 제공자가 OpenAI인 경우 대신 OpenAI provider Plugin과 -`OPENAI_API_KEY`로 `realtime.provider: "openai"`를 사용하세요. +실시간 음성 제공자가 OpenAI라면 대신 OpenAI 제공자 Plugin과 `OPENAI_API_KEY`로 +`realtime.provider: "openai"`를 사용하세요. -`voice-call`을 활성화한 후 Gateway를 다시 시작하거나 다시 로드하세요. -Plugin 구성 변경 사항은 다시 로드되기 전까지 이미 실행 중인 Gateway -프로세스에 나타나지 않습니다. +`voice-call`을 활성화한 뒤 Gateway를 다시 시작하거나 다시 로드하세요. Plugin 구성 +변경 사항은 다시 로드되기 전까지 이미 실행 중인 Gateway 프로세스에 나타나지 +않습니다. 그런 다음 확인하세요. @@ -431,7 +429,7 @@ openclaw googlemeet setup Twilio 위임이 연결되면 `googlemeet setup`에 성공한 `twilio-voice-call-plugin`, `twilio-voice-call-credentials`, -`twilio-voice-call-webhook` 점검이 포함됩니다. +`twilio-voice-call-webhook` 확인이 포함됩니다. ```bash openclaw googlemeet join https://meet.google.com/abc-defg-hij \ @@ -451,36 +449,36 @@ openclaw googlemeet join https://meet.google.com/abc-defg-hij \ ## OAuth 및 사전 점검 -`googlemeet create`가 브라우저 자동화로 대체할 수 있으므로 Meet 링크 생성에 -OAuth는 선택 사항입니다. 공식 API 생성, 공간 확인 또는 Meet Media API 사전 +`googlemeet create`가 브라우저 자동화로 대체될 수 있으므로 Meet 링크 생성에는 +OAuth가 선택 사항입니다. 공식 API 생성, 스페이스 확인 또는 Meet Media API 사전 점검이 필요할 때 OAuth를 구성하세요. -Google Meet API 접근은 사용자 OAuth를 사용합니다. Google Cloud OAuth -클라이언트를 만들고, 필요한 범위를 요청하고, Google 계정을 승인한 다음, -결과 refresh token을 Google Meet Plugin 구성에 저장하거나 -`OPENCLAW_GOOGLE_MEET_*` 환경 변수를 제공하세요. +Google Meet API 액세스는 사용자 OAuth를 사용합니다. Google Cloud OAuth 클라이언트를 +생성하고, 필요한 범위를 요청하고, Google 계정을 승인한 뒤, 생성된 새로 고침 토큰을 +Google Meet Plugin 구성에 저장하거나 `OPENCLAW_GOOGLE_MEET_*` 환경 변수를 +제공하세요. -OAuth는 Chrome 참여 경로를 대체하지 않습니다. Chrome 및 Chrome-node 전송은 -브라우저 참여를 사용할 때도 로그인된 Chrome 프로필, BlackHole/SoX, 연결된 -노드를 통해 참여합니다. OAuth는 공식 Google Meet API 경로에만 사용됩니다. -회의 공간 생성, 공간 확인, Meet Media API 사전 점검 실행입니다. +OAuth는 Chrome 참여 경로를 대체하지 않습니다. 브라우저 참여를 사용할 때 Chrome 및 +Chrome-node 전송 방식은 여전히 로그인된 Chrome 프로필, BlackHole/SoX, 연결된 노드를 +통해 참여합니다. OAuth는 공식 Google Meet API 경로, 즉 회의 스페이스 생성, 스페이스 +확인, Meet Media API 사전 점검 실행에만 사용됩니다. -### Google 자격 증명 만들기 +### Google 자격 증명 생성 Google Cloud Console에서: -1. Google Cloud 프로젝트를 만들거나 선택합니다. -2. 해당 프로젝트에 대해 **Google Meet REST API**를 활성화합니다. +1. Google Cloud 프로젝트를 생성하거나 선택합니다. +2. 해당 프로젝트에 **Google Meet REST API**를 활성화합니다. 3. OAuth 동의 화면을 구성합니다. - - Google Workspace 조직의 경우 **Internal**이 가장 간단합니다. - - 개인/테스트 설정에는 **External**이 작동합니다. 앱이 Testing 상태인 동안, - 앱을 승인할 각 Google 계정을 테스트 사용자로 추가하세요. + - **Internal**은 Google Workspace 조직에 가장 간단합니다. + - **External**은 개인/테스트 설정에 사용할 수 있습니다. 앱이 Testing 상태인 + 동안 앱을 승인할 각 Google 계정을 테스트 사용자로 추가하세요. 4. OpenClaw가 요청하는 범위를 추가합니다. - `https://www.googleapis.com/auth/meetings.space.created` - `https://www.googleapis.com/auth/meetings.space.readonly` - `https://www.googleapis.com/auth/meetings.space.settings` - `https://www.googleapis.com/auth/meetings.conference.media.readonly` -5. OAuth 클라이언트 ID를 만듭니다. +5. OAuth 클라이언트 ID를 생성합니다. - 애플리케이션 유형: **Web application**. - 승인된 리디렉션 URI: @@ -491,27 +489,26 @@ Google Cloud Console에서: 6. 클라이언트 ID와 클라이언트 보안 비밀을 복사합니다. `meetings.space.created`는 Google Meet `spaces.create`에 필요합니다. -`meetings.space.readonly`는 OpenClaw가 Meet URL/코드를 공간으로 확인할 수 -있게 합니다. -`meetings.space.settings`는 OpenClaw가 API 룸 생성 중 `accessType` 같은 -`SpaceConfig` 설정을 전달할 수 있게 합니다. -`meetings.conference.media.readonly`는 Meet Media API 사전 점검 및 미디어 -작업을 위한 것입니다. 실제 Media API 사용에는 Google이 Developer Preview -등록을 요구할 수 있습니다. 브라우저 기반 Chrome 참여만 필요하다면 OAuth를 -완전히 건너뛰세요. +`meetings.space.readonly`를 사용하면 OpenClaw가 Meet URL/코드를 스페이스로 +확인할 수 있습니다. +`meetings.space.settings`를 사용하면 OpenClaw가 API 회의실 생성 중 `accessType` +같은 `SpaceConfig` 설정을 전달할 수 있습니다. +`meetings.conference.media.readonly`는 Meet Media API 사전 점검 및 미디어 작업을 +위한 것입니다. 실제 Media API 사용에는 Google이 Developer Preview 등록을 요구할 수 +있습니다. 브라우저 기반 Chrome 참여만 필요하다면 OAuth를 완전히 건너뛰세요. -### refresh token 발급 +### 새로 고침 토큰 발급 -`oauth.clientId`와 선택적으로 `oauth.clientSecret`을 구성하거나 환경 변수로 -전달한 다음 실행하세요. +`oauth.clientId` 및 선택적으로 `oauth.clientSecret`을 구성하거나 환경 변수로 +전달한 뒤 다음을 실행하세요. ```bash openclaw googlemeet auth login --json ``` -이 명령은 refresh token이 포함된 `oauth` 구성 블록을 출력합니다. PKCE, -`http://localhost:8085/oauth2callback`의 localhost 콜백, 그리고 `--manual`을 -사용한 수동 복사/붙여넣기 흐름을 사용합니다. +이 명령은 새로 고침 토큰이 포함된 `oauth` 구성 블록을 출력합니다. PKCE, +`http://localhost:8085/oauth2callback`의 localhost 콜백, 그리고 `--manual`을 통한 +수동 복사/붙여넣기 흐름을 사용합니다. 예: @@ -521,7 +518,7 @@ OPENCLAW_GOOGLE_MEET_CLIENT_SECRET="your-client-secret" \ openclaw googlemeet auth login --json ``` -브라우저가 로컬 콜백에 도달할 수 없을 때는 수동 모드를 사용하세요. +브라우저가 로컬 콜백에 도달할 수 없을 때 수동 모드를 사용하세요. ```bash OPENCLAW_GOOGLE_MEET_CLIENT_ID="your-client-id" \ @@ -565,60 +562,67 @@ JSON 출력에는 다음이 포함됩니다. } ``` -refresh token을 구성에 넣고 싶지 않을 때는 환경 변수를 선호하세요. -구성과 환경 값이 모두 있으면 Plugin은 먼저 구성을 확인한 다음 환경으로 -대체합니다. +구성에 새로 고침 토큰을 두고 싶지 않으면 환경 변수를 선호하세요. 구성 값과 환경 +값이 모두 있으면 Plugin은 먼저 구성을 해석한 뒤 환경 대체값을 사용합니다. -OAuth 동의에는 Meet 공간 생성, Meet 공간 읽기 접근, Meet 회의 미디어 읽기 -접근이 포함됩니다. 회의 생성 지원이 생기기 전에 인증했다면 refresh token에 -`meetings.space.created` 범위가 있도록 `openclaw googlemeet auth login --json`을 -다시 실행하세요. +OAuth 동의에는 Meet 스페이스 생성, Meet 스페이스 읽기 액세스, Meet 회의 미디어 +읽기 액세스가 포함됩니다. 회의 생성 지원이 존재하기 전에 인증했다면 새로 고침 +토큰에 `meetings.space.created` 범위가 포함되도록 +`openclaw googlemeet auth login --json`을 다시 실행하세요. ### doctor로 OAuth 확인 -빠른 비밀 값 없는 상태 점검이 필요할 때 OAuth doctor를 실행하세요. +빠른 비밀 없는 상태 확인이 필요할 때 OAuth doctor를 실행하세요. ```bash openclaw googlemeet doctor --oauth --json ``` -이 명령은 Chrome 런타임을 로드하지 않으며 연결된 Chrome 노드가 필요하지 -않습니다. OAuth 구성이 존재하는지와 refresh token이 access token을 발급할 수 -있는지 확인합니다. JSON 보고서에는 `ok`, `configured`, `tokenSource`, -`expiresAt`, 점검 메시지 같은 상태 필드만 포함됩니다. access token, -refresh token 또는 클라이언트 보안 비밀은 출력하지 않습니다. +이 명령은 Chrome 런타임을 로드하지 않으며 연결된 Chrome 노드도 필요하지 않습니다. +OAuth 구성이 존재하는지, 새로 고침 토큰으로 액세스 토큰을 발급할 수 있는지 +확인합니다. JSON 보고서에는 `ok`, `configured`, `tokenSource`, `expiresAt`, 확인 +메시지 같은 상태 필드만 포함되며, 액세스 토큰, 새로 고침 토큰 또는 클라이언트 +보안 비밀은 출력하지 않습니다. 일반적인 결과: -| 검사 | 의미 | -| -------------------- | --------------------------------------------------------------------------------------- | -| `oauth-config` | `oauth.clientId`와 `oauth.refreshToken`, 또는 캐시된 액세스 토큰이 있습니다. | -| `oauth-token` | 캐시된 액세스 토큰이 아직 유효하거나, 새로고침 토큰이 새 액세스 토큰을 발급했습니다. | -| `meet-spaces-get` | 선택적 `--meeting` 검사가 기존 Meet 공간을 확인했습니다. | -| `meet-spaces-create` | 선택적 `--create-space` 검사가 새 Meet 공간을 만들었습니다. | +| 검사 | 의미 | +| -------------------- | ------------------------------------------------------------------------------------------ | +| `oauth-config` | `oauth.clientId`와 `oauth.refreshToken`, 또는 캐시된 액세스 토큰이 있습니다. | +| `oauth-token` | 캐시된 액세스 토큰이 아직 유효하거나, refresh token이 새 액세스 토큰을 발급했습니다. | +| `meet-spaces-get` | 선택적 `--meeting` 검사가 기존 Meet 공간을 확인했습니다. | +| `meet-spaces-create` | 선택적 `--create-space` 검사가 새 Meet 공간을 만들었습니다. | -Google Meet API 활성화와 `spaces.create` 범위도 증명하려면 부수 효과가 있는 만들기 검사를 실행하세요. +Google Meet API 활성화와 `spaces.create` 스코프도 증명하려면, 부수 효과가 있는 +생성 검사를 실행하세요. ```bash openclaw googlemeet doctor --oauth --create-space --json openclaw googlemeet create --no-join --json ``` -`--create-space`는 일회용 Meet URL을 만듭니다. Google Cloud 프로젝트에 Meet API가 활성화되어 있고 승인된 계정에 `meetings.space.created` 범위가 있는지 확인해야 할 때 사용하세요. +`--create-space`는 일회용 Meet URL을 만듭니다. Google Cloud 프로젝트에 Meet API가 +활성화되어 있고 승인된 계정에 `meetings.space.created` 스코프가 있는지 확인해야 할 +때 사용하세요. -기존 회의 공간에 대한 읽기 액세스를 증명하려면 다음을 실행하세요. +기존 회의 공간에 대한 읽기 액세스를 증명하려면: ```bash openclaw googlemeet doctor --oauth --meeting https://meet.google.com/abc-defg-hij --json openclaw googlemeet resolve-space --meeting https://meet.google.com/abc-defg-hij ``` -`doctor --oauth --meeting` 및 `resolve-space`는 승인된 Google 계정이 액세스할 수 있는 기존 공간에 대한 읽기 액세스를 증명합니다. 이러한 검사에서 `403`이 반환되면 일반적으로 Google Meet REST API가 비활성화되어 있거나, 동의한 새로고침 토큰에 필요한 범위가 없거나, Google 계정이 해당 Meet 공간에 액세스할 수 없다는 의미입니다. 새로고침 토큰 오류는 `openclaw googlemeet auth login ---json`을 다시 실행하고 새 `oauth` 블록을 저장해야 함을 의미합니다. +`doctor --oauth --meeting`과 `resolve-space`는 승인된 Google 계정이 액세스할 수 +있는 기존 공간에 대한 읽기 액세스를 증명합니다. 이러한 검사에서 `403`이 발생하면 +일반적으로 Google Meet REST API가 비활성화되어 있거나, 동의된 refresh token에 +필수 스코프가 없거나, Google 계정이 해당 Meet 공간에 액세스할 수 없다는 뜻입니다. +refresh-token 오류는 `openclaw googlemeet auth login --json`을 다시 실행하고 새 +`oauth` 블록을 저장하라는 뜻입니다. -브라우저 대체 모드에는 OAuth 자격 증명이 필요하지 않습니다. 이 모드에서 Google 인증은 OpenClaw 구성에서가 아니라 선택한 Node의 로그인된 Chrome 프로필에서 가져옵니다. +브라우저 폴백에는 OAuth 자격 증명이 필요하지 않습니다. 이 모드에서는 Google 인증이 +OpenClaw config가 아니라 선택된 Node에 로그인된 Chrome 프로필에서 가져옵니다. -다음 환경 변수를 대체값으로 사용할 수 있습니다. +다음 환경 변수는 폴백으로 허용됩니다. - `OPENCLAW_GOOGLE_MEET_CLIENT_ID` 또는 `GOOGLE_MEET_CLIENT_ID` - `OPENCLAW_GOOGLE_MEET_CLIENT_SECRET` 또는 `GOOGLE_MEET_CLIENT_SECRET` @@ -629,19 +633,19 @@ openclaw googlemeet resolve-space --meeting https://meet.google.com/abc-defg-hij - `OPENCLAW_GOOGLE_MEET_DEFAULT_MEETING` 또는 `GOOGLE_MEET_DEFAULT_MEETING` - `OPENCLAW_GOOGLE_MEET_PREVIEW_ACK` 또는 `GOOGLE_MEET_PREVIEW_ACK` -Meet URL, 코드 또는 `spaces/{id}`를 `spaces.get`을 통해 확인하세요. +Meet URL, 코드, 또는 `spaces/{id}`를 `spaces.get`을 통해 해석하세요. ```bash openclaw googlemeet resolve-space --meeting https://meet.google.com/abc-defg-hij ``` -미디어 작업 전에 사전 점검을 실행하세요. +미디어 작업 전에 사전 검사를 실행하세요. ```bash openclaw googlemeet preflight --meeting https://meet.google.com/abc-defg-hij ``` -Meet에서 회의 기록을 만든 후 회의 아티팩트와 참석 정보를 나열하세요. +Meet가 회의 기록을 만든 후 회의 산출물과 참석 정보를 나열하세요. ```bash openclaw googlemeet artifacts --meeting https://meet.google.com/abc-defg-hij @@ -649,9 +653,12 @@ openclaw googlemeet attendance --meeting https://meet.google.com/abc-defg-hij openclaw googlemeet export --meeting https://meet.google.com/abc-defg-hij --output ./meet-export ``` -`--meeting`을 사용하면 `artifacts` 및 `attendance`는 기본적으로 최신 회의 기록을 사용합니다. 해당 회의의 보존된 모든 기록을 원할 때는 `--all-conference-records`를 전달하세요. +`--meeting`을 사용하면 `artifacts`와 `attendance`는 기본적으로 최신 회의 기록을 +사용합니다. 해당 회의에 보존된 모든 기록을 원하면 `--all-conference-records`를 +전달하세요. -캘린더 조회는 Meet 아티팩트를 읽기 전에 Google Calendar에서 회의 URL을 확인할 수 있습니다. +Calendar 조회는 Meet 산출물을 읽기 전에 Google Calendar에서 회의 URL을 확인할 수 +있습니다. ```bash openclaw googlemeet latest --today @@ -660,10 +667,14 @@ openclaw googlemeet artifacts --event "Weekly sync" openclaw googlemeet attendance --today --format csv --output attendance.csv ``` -`--today`는 오늘의 `primary` 캘린더에서 Google Meet 링크가 있는 Calendar 이벤트를 검색합니다. 일치하는 이벤트 텍스트를 검색하려면 `--event `를 사용하고, 기본 캘린더가 아닌 캘린더에는 `--calendar `를 사용하세요. 캘린더 조회에는 Calendar 이벤트 읽기 전용 범위를 포함하는 새로운 OAuth 로그인이 필요합니다. -`calendar-events`는 일치하는 Meet 이벤트를 미리 보여 주고 `latest`, `artifacts`, `attendance` 또는 `export`가 선택할 이벤트를 표시합니다. +`--today`는 오늘의 `primary` 캘린더에서 Google Meet 링크가 있는 Calendar 이벤트를 +검색합니다. 일치하는 이벤트 텍스트를 검색하려면 `--event `를 사용하고, +기본 캘린더가 아닌 캘린더에는 `--calendar `를 사용하세요. Calendar 조회에는 +Calendar events readonly 스코프가 포함된 새 OAuth 로그인이 필요합니다. +`calendar-events`는 일치하는 Meet 이벤트를 미리 보여 주고 `latest`, `artifacts`, +`attendance`, 또는 `export`가 선택할 이벤트를 표시합니다. -이미 회의 기록 ID를 알고 있다면 직접 지정하세요. +회의 기록 ID를 이미 알고 있다면 직접 지정하세요. ```bash openclaw googlemeet latest --meeting https://meet.google.com/abc-defg-hij @@ -677,9 +688,13 @@ openclaw googlemeet attendance --conference-record conferenceRecords/abc123 --js openclaw googlemeet end-active-conference https://meet.google.com/abc-defg-hij ``` -이는 Google Meet `spaces.endActiveConference`를 호출하며 승인된 계정이 관리할 수 있는 공간에 대해 `meetings.space.created` 범위가 포함된 OAuth가 필요합니다. -OpenClaw는 Meet URL, 회의 코드 또는 `spaces/{id}` 입력을 받아 활성 회의를 종료하기 전에 API 공간 리소스로 확인합니다. -이는 `googlemeet leave`와 별개입니다. `leave`는 OpenClaw의 로컬/세션 참여를 중지하는 반면, `end-active-conference`는 Google Meet에 해당 공간의 활성 회의를 종료하도록 요청합니다. +이는 Google Meet `spaces.endActiveConference`를 호출하며, 승인된 계정이 관리할 수 +있는 공간에 대해 `meetings.space.created` 스코프가 있는 OAuth가 필요합니다. +OpenClaw는 Meet URL, 회의 코드, 또는 `spaces/{id}` 입력을 허용하고 활성 회의를 +종료하기 전에 이를 API 공간 리소스로 해석합니다. +이는 `googlemeet leave`와 별개입니다. `leave`는 OpenClaw의 로컬/세션 참여를 +중지하는 반면, `end-active-conference`는 Google Meet에 해당 공간의 활성 회의를 +종료하도록 요청합니다. 읽기 쉬운 보고서를 작성하세요. @@ -696,13 +711,31 @@ openclaw googlemeet export --conference-record conferenceRecords/abc123 \ --include-doc-bodies --dry-run ``` -Google이 회의에 대해 노출하는 경우 `artifacts`는 회의 기록 메타데이터와 참가자, 녹화, 스크립트, 구조화된 스크립트 항목, 스마트 노트 리소스 메타데이터를 반환합니다. 큰 회의에서 항목 조회를 건너뛰려면 `--no-transcript-entries`를 사용하세요. `attendance`는 참가자를 첫/마지막 확인 시간, 총 세션 지속 시간, 지각/조기 퇴장 플래그, 로그인한 사용자 또는 표시 이름으로 병합된 중복 참가자 리소스가 포함된 참가자 세션 행으로 확장합니다. 원시 참가자 리소스를 분리된 상태로 유지하려면 `--no-merge-duplicates`를, 지각 감지를 조정하려면 `--late-after-minutes`를, 조기 퇴장 감지를 조정하려면 `--early-before-minutes`를 전달하세요. +`artifacts`는 Google이 해당 회의에 대해 노출하는 경우, 회의 기록 메타데이터와 +참가자, 녹화, 대화록, 구조화된 대화록 항목, 스마트 노트 리소스 메타데이터를 +반환합니다. 큰 회의에서 항목 조회를 건너뛰려면 `--no-transcript-entries`를 +사용하세요. `attendance`는 참가자를 참가자 세션 행으로 확장하며 최초/최종 확인 +시간, 총 세션 시간, 지각/조기 퇴장 플래그, 로그인 사용자 또는 표시 이름 기준으로 +병합된 중복 참가자 리소스를 포함합니다. 원시 참가자 리소스를 별도로 유지하려면 +`--no-merge-duplicates`를 전달하고, 지각 감지를 조정하려면 `--late-after-minutes`, +조기 퇴장 감지를 조정하려면 `--early-before-minutes`를 전달하세요. -`export`는 `summary.md`, `attendance.csv`, `transcript.md`, `artifacts.json`, `attendance.json`, `manifest.json`이 들어 있는 폴더를 작성합니다. -`manifest.json`은 선택한 입력, 내보내기 옵션, 회의 기록, 출력 파일, 개수, 토큰 소스, 사용된 경우 Calendar 이벤트, 부분 검색 경고를 기록합니다. 폴더 옆에 이식 가능한 아카이브도 작성하려면 `--zip`을 전달하세요. 연결된 스크립트 및 스마트 노트 Google Docs 텍스트를 Google Drive `files.export`를 통해 내보내려면 `--include-doc-bodies`를 전달하세요. 여기에는 Drive Meet 읽기 전용 범위를 포함하는 새로운 OAuth 로그인이 필요합니다. `--include-doc-bodies`가 없으면 내보내기에는 Meet 메타데이터와 구조화된 스크립트 항목만 포함됩니다. Google이 스마트 노트 목록, 스크립트 항목 또는 Drive 문서 본문 오류와 같은 부분 아티팩트 실패를 반환하면, 전체 내보내기를 실패시키지 않고 요약과 매니페스트에 경고를 유지합니다. -폴더나 ZIP을 만들지 않고 동일한 아티팩트/참석 데이터를 가져와 매니페스트 JSON을 출력하려면 `--dry-run`을 사용하세요. 큰 내보내기를 작성하기 전이나 에이전트에 개수, 선택된 기록, 경고만 필요할 때 유용합니다. +`export`는 `summary.md`, `attendance.csv`, `transcript.md`, `artifacts.json`, +`attendance.json`, `manifest.json`이 들어 있는 폴더를 씁니다. `manifest.json`은 +선택된 입력, 내보내기 옵션, 회의 기록, 출력 파일, 개수, 토큰 소스, 사용된 경우의 +Calendar 이벤트, 부분 검색 경고를 기록합니다. 폴더 옆에 이식 가능한 아카이브도 +쓰려면 `--zip`을 전달하세요. 연결된 대화록과 스마트 노트 Google Docs 텍스트를 +Google Drive `files.export`를 통해 내보내려면 `--include-doc-bodies`를 전달하세요. +이를 위해서는 Drive Meet readonly 스코프가 포함된 새 OAuth 로그인이 필요합니다. +`--include-doc-bodies`가 없으면 내보내기에는 Meet 메타데이터와 구조화된 대화록 +항목만 포함됩니다. Google이 스마트 노트 목록, 대화록 항목, 또는 Drive 문서 본문 +오류 같은 부분 산출물 실패를 반환하면, 요약과 매니페스트는 전체 내보내기를 +실패시키는 대신 경고를 유지합니다. +폴더나 ZIP을 만들지 않고 동일한 산출물/참석 데이터를 가져와 매니페스트 JSON을 +출력하려면 `--dry-run`을 사용하세요. 이는 큰 내보내기를 작성하기 전이나 에이전트에 +개수, 선택된 기록, 경고만 필요할 때 유용합니다. -에이전트는 `google_meet` 도구를 통해 동일한 번들도 만들 수 있습니다. +에이전트는 `google_meet` 도구를 통해 같은 번들을 만들 수도 있습니다. ```json { @@ -714,9 +747,10 @@ Google이 회의에 대해 노출하는 경우 `artifacts`는 회의 기록 메 } ``` -파일 쓰기를 건너뛰고 내보내기 매니페스트만 반환하려면 `"dryRun": true`를 설정하세요. +내보내기 매니페스트만 반환하고 파일 쓰기를 건너뛰려면 `"dryRun": true`를 +설정하세요. -에이전트는 명시적 액세스 정책으로 API 기반 방도 만들 수 있습니다. +에이전트는 명시적 액세스 정책으로 API 기반 방을 만들 수도 있습니다. ```json { @@ -727,7 +761,7 @@ Google이 회의에 대해 노출하는 경우 `artifacts`는 회의 기록 메 } ``` -또한 알려진 방의 활성 회의를 종료할 수도 있습니다. +또한 알려진 방의 활성 회의를 종료할 수 있습니다. ```json { @@ -736,7 +770,8 @@ Google이 회의에 대해 노출하는 경우 `artifacts`는 회의 기록 메 } ``` -먼저 듣기 검증을 위해 에이전트는 회의가 유용하다고 주장하기 전에 `test_listen`을 사용해야 합니다. +듣기 우선 검증의 경우, 에이전트는 회의가 유용하다고 주장하기 전에 `test_listen`을 +사용해야 합니다. ```json { @@ -747,7 +782,7 @@ Google이 회의에 대해 노출하는 경우 `artifacts`는 회의 기록 메 } ``` -실제 보존된 회의에 대해 보호된 라이브 스모크를 실행하세요. +실제로 보존된 회의를 대상으로 보호된 라이브 스모크를 실행하세요. ```bash OPENCLAW_LIVE_TEST=1 \ @@ -755,7 +790,8 @@ OPENCLAW_GOOGLE_MEET_LIVE_MEETING=https://meet.google.com/abc-defg-hij \ pnpm test:live -- extensions/google-meet/google-meet.live.test.ts ``` -누군가 말하고 Meet 자막을 사용할 수 있는 회의에 대해 라이브 먼저 듣기 브라우저 프로브를 실행하세요. +Meet 자막을 사용할 수 있고 누군가 말할 회의를 대상으로 라이브 듣기 우선 브라우저 +프로브를 실행하세요. ```bash openclaw googlemeet setup --transport chrome-node --mode transcribe @@ -765,19 +801,21 @@ openclaw googlemeet test-listen https://meet.google.com/abc-defg-hij --transport 라이브 스모크 환경: - `OPENCLAW_LIVE_TEST=1`은 보호된 라이브 테스트를 활성화합니다. -- `OPENCLAW_GOOGLE_MEET_LIVE_MEETING`은 보존된 Meet URL, 코드 또는 +- `OPENCLAW_GOOGLE_MEET_LIVE_MEETING`은 보존된 Meet URL, 코드, 또는 `spaces/{id}`를 가리킵니다. - `OPENCLAW_GOOGLE_MEET_CLIENT_ID` 또는 `GOOGLE_MEET_CLIENT_ID`는 OAuth - 클라이언트 ID를 제공합니다. + client id를 제공합니다. - `OPENCLAW_GOOGLE_MEET_REFRESH_TOKEN` 또는 `GOOGLE_MEET_REFRESH_TOKEN`은 - 새로고침 토큰을 제공합니다. + refresh token을 제공합니다. - 선택 사항: `OPENCLAW_GOOGLE_MEET_CLIENT_SECRET`, - `OPENCLAW_GOOGLE_MEET_ACCESS_TOKEN`, 및 - `OPENCLAW_GOOGLE_MEET_ACCESS_TOKEN_EXPIRES_AT`은 `OPENCLAW_` 접두사가 없는 동일한 대체 이름을 사용합니다. + `OPENCLAW_GOOGLE_MEET_ACCESS_TOKEN`, `OPENCLAW_GOOGLE_MEET_ACCESS_TOKEN_EXPIRES_AT`는 + `OPENCLAW_` 접두사 없이 동일한 폴백 이름을 사용합니다. -기본 아티팩트/참석 라이브 스모크에는 -`https://www.googleapis.com/auth/meetings.space.readonly` 및 -`https://www.googleapis.com/auth/meetings.conference.media.readonly`가 필요합니다. 캘린더 조회에는 `https://www.googleapis.com/auth/calendar.events.readonly`가 필요합니다. Drive 문서 본문 내보내기에는 +기본 산출물/참석 라이브 스모크에는 +`https://www.googleapis.com/auth/meetings.space.readonly`와 +`https://www.googleapis.com/auth/meetings.conference.media.readonly`가 필요합니다. +Calendar 조회에는 `https://www.googleapis.com/auth/calendar.events.readonly`가 +필요합니다. Drive 문서 본문 내보내기에는 `https://www.googleapis.com/auth/drive.meet.readonly`가 필요합니다. 새 Meet 공간을 만드세요. @@ -786,9 +824,13 @@ openclaw googlemeet test-listen https://meet.google.com/abc-defg-hij --transport openclaw googlemeet create ``` -이 명령은 새 `meeting uri`, 소스, 참여 세션을 출력합니다. OAuth 자격 증명이 있으면 공식 Google Meet API를 사용합니다. OAuth 자격 증명이 없으면 고정된 Chrome Node의 로그인된 브라우저 프로필을 대체 수단으로 사용합니다. 에이전트는 `action: "create"`와 함께 `google_meet` 도구를 사용하여 한 단계로 만들고 참여할 수 있습니다. URL만 만들려면 `"join": false`를 전달하세요. +명령은 새 `meeting uri`, 소스, 참여 세션을 출력합니다. OAuth 자격 증명이 있으면 +공식 Google Meet API를 사용합니다. OAuth 자격 증명이 없으면 고정된 Chrome Node의 +로그인된 브라우저 프로필을 폴백으로 사용합니다. 에이전트는 `action: "create"`와 +함께 `google_meet` 도구를 사용해 한 단계에서 만들고 참여할 수 있습니다. URL만 +만들려면 `"join": false`를 전달하세요. -브라우저 대체 모드의 예시 JSON 출력: +브라우저 폴백의 JSON 출력 예: ```json { @@ -808,7 +850,9 @@ openclaw googlemeet create } ``` -브라우저 대체 모드가 URL을 만들기 전에 Google 로그인 또는 Meet 권한 차단에 걸리면 Gateway 메서드는 실패한 응답을 반환하고 `google_meet` 도구는 일반 문자열 대신 구조화된 세부 정보를 반환합니다. +브라우저 폴백이 URL을 만들기 전에 Google 로그인 또는 Meet 권한 차단에 걸리면, +Gateway 메서드는 실패 응답을 반환하고 `google_meet` 도구는 일반 문자열 대신 +구조화된 세부 정보를 반환합니다. ```json { @@ -826,9 +870,11 @@ openclaw googlemeet create } ``` -에이전트가 `manualActionRequired: true`를 보면 `manualActionMessage`와 브라우저 Node/탭 컨텍스트를 보고하고, 운영자가 브라우저 단계를 완료할 때까지 새 Meet 탭을 열지 않아야 합니다. +에이전트가 `manualActionRequired: true`를 보면 `manualActionMessage`와 브라우저 +Node/탭 컨텍스트를 보고하고, 운영자가 브라우저 단계를 완료할 때까지 새 Meet 탭을 +열지 않아야 합니다. -API 만들기의 예시 JSON 출력: +API 생성의 JSON 출력 예: ```json { @@ -849,23 +895,23 @@ API 만들기의 예시 JSON 출력: } ``` -Meet를 만들면 기본적으로 참여합니다. Chrome 또는 Chrome-node 전송 방식은 -브라우저를 통해 참여하기 위해 로그인된 Google Chrome 프로필이 여전히 -필요합니다. 프로필이 로그아웃되어 있으면 OpenClaw는 -`manualActionRequired: true` 또는 브라우저 폴백 오류를 보고하고, 재시도하기 -전에 운영자가 Google 로그인을 완료하도록 요청합니다. +Meet를 만들면 기본적으로 참여합니다. Chrome 또는 Chrome-node 전송은 여전히 +브라우저를 통해 참여하려면 로그인된 Google Chrome 프로필이 필요합니다. 프로필이 +로그아웃되어 있으면 OpenClaw는 `manualActionRequired: true` 또는 브라우저 +대체 오류를 보고하고, 재시도하기 전에 운영자에게 Google 로그인을 완료하라고 +요청합니다. -Cloud 프로젝트, OAuth 주체, 회의 참가자가 Meet 미디어 API용 Google +Cloud 프로젝트, OAuth 주체, 회의 참여자가 Meet 미디어 API용 Google Workspace Developer Preview Program에 등록되어 있음을 확인한 후에만 `preview.enrollmentAcknowledged: true`를 설정하세요. ## 구성 -공통 Chrome 에이전트 경로에는 Plugin 활성화, BlackHole, SoX, 실시간 전사 -제공자 키, 구성된 OpenClaw TTS 제공자만 필요합니다. OpenAI가 기본 전사 -제공자입니다. 기본 에이전트 모드 전사 제공자를 변경하지 않고 `bidi` -모드에서 Google Gemini Live를 사용하려면 `realtime.voiceProvider`를 -`"google"`로, `realtime.model`을 설정하세요. +공통 Chrome 에이전트 경로에는 Plugin 활성화, BlackHole, SoX, 실시간 +트랜스크립션 제공자 키, 구성된 OpenClaw TTS 제공자만 필요합니다. OpenAI가 +기본 트랜스크립션 제공자입니다. 기본 에이전트 모드 트랜스크립션 제공자를 +변경하지 않고 `bidi` 모드에서 Google Gemini Live를 사용하려면 +`realtime.voiceProvider`를 `"google"`로, `realtime.model`을 설정하세요. ```bash brew install blackhole-2ch sox @@ -874,7 +920,7 @@ export OPENAI_API_KEY=sk-... export GEMINI_API_KEY=... ``` -Plugin 구성을 `plugins.entries.google-meet.config` 아래에 설정하세요. +Plugin 구성을 `plugins.entries.google-meet.config` 아래에 설정합니다. ```json5 { @@ -892,61 +938,57 @@ Plugin 구성을 `plugins.entries.google-meet.config` 아래에 설정하세요. 기본값: - `defaultTransport: "chrome"` -- `defaultMode: "agent"`(`"realtime"`은 `"agent"`의 레거시 호환 별칭으로만 - 허용됩니다. 새 도구 호출에서는 `"agent"`를 사용해야 합니다.) +- `defaultMode: "agent"` (`"realtime"`은 `"agent"`의 레거시 호환성 + 별칭으로만 허용됩니다. 새 도구 호출은 `"agent"`를 사용해야 합니다.) - `chromeNode.node`: `chrome-node`의 선택적 노드 ID/이름/IP - `chrome.audioBackend: "blackhole-2ch"` - `chrome.guestName: "OpenClaw Agent"`: 로그아웃된 Meet 게스트 화면에서 사용하는 이름 -- `chrome.autoJoin: true`: `chrome-node`의 OpenClaw 브라우저 자동화를 통해 - 최선 노력 방식으로 게스트 이름을 입력하고 지금 참여를 클릭합니다. -- `chrome.reuseExistingTab: true`: 중복 탭을 여는 대신 기존 Meet 탭을 - 활성화합니다. -- `chrome.waitForInCallMs: 20000`: 응답 소개가 트리거되기 전에 Meet 탭이 - 통화 중임을 보고할 때까지 기다립니다. -- `chrome.audioFormat: "pcm16-24khz"`: 명령 쌍 오디오 형식입니다. 아직 전화 +- `chrome.autoJoin: true`: `chrome-node`에서 OpenClaw 브라우저 자동화를 + 통해 게스트 이름 채우기 및 지금 참여 클릭을 최선의 방식으로 수행 +- `chrome.reuseExistingTab: true`: 중복 탭을 여는 대신 기존 Meet 탭 활성화 +- `chrome.waitForInCallMs: 20000`: 대화형 인트로가 트리거되기 전에 Meet 탭이 + 통화 중임을 보고할 때까지 대기 +- `chrome.audioFormat: "pcm16-24khz"`: 명령 쌍 오디오 형식. 여전히 전화 오디오를 내보내는 레거시/사용자 지정 명령 쌍에만 `"g711-ulaw-8khz"`를 사용하세요. -- `chrome.audioBufferBytes: 4096`: 생성된 Chrome 명령 쌍 오디오 명령의 SoX - 처리 버퍼입니다. 이는 SoX 기본 8192바이트 버퍼의 절반으로, 기본 파이프 - 지연 시간을 줄이면서 바쁜 호스트에서 값을 높일 여지를 남깁니다. SoX - 최솟값보다 낮은 값은 17바이트로 제한됩니다. +- `chrome.audioBufferBytes: 4096`: 생성된 Chrome 명령 쌍 오디오 명령을 위한 + SoX 처리 버퍼입니다. 이는 SoX 기본 8192바이트 버퍼의 절반으로, 사용량이 많은 + 호스트에서 높일 여지를 남기면서 기본 파이프 지연 시간을 줄입니다. SoX 최소값 + 미만의 값은 17바이트로 고정됩니다. - `chrome.audioInputCommand`: CoreAudio `BlackHole 2ch`에서 읽고 `chrome.audioFormat`으로 오디오를 쓰는 SoX 명령 -- `chrome.audioOutputCommand`: `chrome.audioFormat`의 오디오를 읽고 CoreAudio - `BlackHole 2ch`에 쓰는 SoX 명령 -- `chrome.bargeInInputCommand`: 어시스턴트 재생이 활성 상태일 때 사람의 - 끼어들기 감지를 위해 부호 있는 16비트 리틀 엔디언 모노 PCM을 쓰는 - 선택적 로컬 마이크 명령입니다. 현재 이는 Gateway 호스팅 `chrome` 명령 - 쌍 브리지에 적용됩니다. +- `chrome.audioOutputCommand`: `chrome.audioFormat`으로 오디오를 읽고 + CoreAudio `BlackHole 2ch`로 쓰는 SoX 명령 +- `chrome.bargeInInputCommand`: 어시스턴트 재생이 활성 상태일 때 사람의 끼어들기 + 감지를 위해 부호 있는 16비트 리틀 엔디언 모노 PCM을 쓰는 선택적 로컬 마이크 + 명령입니다. 현재 이는 Gateway 호스팅 `chrome` 명령 쌍 브리지에 적용됩니다. - `chrome.bargeInRmsThreshold: 650`: `chrome.bargeInInputCommand`에서 사람의 - 끼어들기로 간주되는 RMS 수준 -- `chrome.bargeInPeakThreshold: 2500`: `chrome.bargeInInputCommand`에서 - 사람의 끼어들기로 간주되는 피크 수준 -- `chrome.bargeInCooldownMs: 900`: 반복되는 사람 끼어들기 해제 사이의 최소 - 지연 시간 -- `mode: "agent"`: 기본 응답 모드입니다. 참가자 음성은 구성된 실시간 전사 - 제공자가 전사하고, 회의별 하위 에이전트 세션의 구성된 OpenClaw - 에이전트로 전송되며, 일반 OpenClaw TTS 런타임을 통해 음성으로 - 출력됩니다. -- `mode: "bidi"`: 폴백 직접 양방향 실시간 모델 모드입니다. 실시간 음성 - 제공자가 참가자 음성에 직접 응답하며, 더 깊거나 도구 기반인 답변을 위해 + 중단으로 간주되는 RMS 수준 +- `chrome.bargeInPeakThreshold: 2500`: `chrome.bargeInInputCommand`에서 사람의 + 중단으로 간주되는 피크 수준 +- `chrome.bargeInCooldownMs: 900`: 반복적인 사람 중단 해제 사이의 최소 지연 +- `mode: "agent"`: 기본 대화형 모드. 참여자 발화는 구성된 실시간 트랜스크립션 + 제공자가 전사하고, 회의별 하위 에이전트 세션의 구성된 OpenClaw 에이전트로 + 전송되며, 일반 OpenClaw TTS 런타임을 통해 다시 말해집니다. +- `mode: "bidi"`: 대체 직접 양방향 실시간 모델 모드. 실시간 음성 제공자는 + 참여자 발화에 직접 응답하며 더 깊거나 도구 기반 답변을 위해 `openclaw_agent_consult`를 호출할 수 있습니다. -- `mode: "transcribe"`: 응답 브리지 없는 관찰 전용 모드입니다. -- `realtime.provider: "openai"`: 아래의 범위 지정 제공자 필드가 설정되지 - 않았을 때 사용되는 호환성 폴백입니다. -- `realtime.transcriptionProvider: "openai"`: `agent` 모드가 실시간 전사에 - 사용하는 제공자 ID입니다. -- `realtime.voiceProvider`: `bidi` 모드가 직접 실시간 음성에 사용하는 제공자 - ID입니다. 에이전트 모드 전사는 OpenAI로 유지하면서 Gemini Live를 - 사용하려면 이를 `"google"`로 설정하세요. +- `mode: "transcribe"`: 대화형 브리지 없는 관찰 전용 모드. +- `realtime.provider: "openai"`: 아래 범위 지정 제공자 필드가 설정되지 않았을 때 + 사용하는 호환성 대체값. +- `realtime.transcriptionProvider: "openai"`: `agent` 모드가 실시간 + 트랜스크립션에 사용하는 제공자 ID. +- `realtime.voiceProvider`: `bidi` 모드가 직접 실시간 음성에 사용하는 제공자 ID. + 에이전트 모드 트랜스크립션을 OpenAI에 유지하면서 Gemini Live를 사용하려면 이를 + `"google"`로 설정하세요. - `realtime.toolPolicy: "safe-read-only"` -- `realtime.instructions`: 더 깊은 답변에는 `openclaw_agent_consult`를 +- `realtime.instructions`: 더 깊은 답변을 위해 `openclaw_agent_consult`를 사용하는 짧은 음성 응답 -- `realtime.introMessage`: 실시간 브리지가 연결될 때의 짧은 음성 준비 확인 - 메시지입니다. 조용히 참여하려면 `""`로 설정하세요. -- `realtime.agentId`: `openclaw_agent_consult`용 선택적 OpenClaw 에이전트 - ID입니다. 기본값은 `main`입니다. +- `realtime.introMessage`: 실시간 브리지가 연결될 때의 짧은 음성 준비 확인. + 조용히 참여하려면 `""`로 설정하세요. +- `realtime.agentId`: `openclaw_agent_consult`용 선택적 OpenClaw 에이전트 ID. + 기본값은 `main`입니다. 선택적 재정의: @@ -1038,11 +1080,11 @@ Plugin 구성을 `plugins.entries.google-meet.config` 아래에 설정하세요. } ``` -지속되는 Meet 음성은 `messages.tts.providers.elevenlabs.voiceId`에서 옵니다. -TTS 모델 재정의가 활성화된 경우 에이전트 응답은 응답별 -`[[tts:voiceId=... model=eleven_v3]]` 지시문도 사용할 수 있지만, 회의의 -결정적 기본값은 구성입니다. 참여 시 로그에는 -`transcriptionProvider=elevenlabs`가 표시되어야 하며, 각 음성 응답은 +지속적인 Meet 음성은 `messages.tts.providers.elevenlabs.voiceId`에서 +옵니다. TTS 모델 재정의가 활성화되어 있으면 에이전트 응답은 응답별 +`[[tts:voiceId=... model=eleven_v3]]` 지시문도 사용할 수 있지만, 회의에서는 +구성이 결정적 기본값입니다. 참여 시 로그에는 `transcriptionProvider=elevenlabs`가 +표시되어야 하며, 각 음성 응답은 `provider=elevenlabs model=eleven_v3 voice=`를 기록해야 합니다. Twilio 전용 구성: @@ -1060,12 +1102,11 @@ Twilio 전용 구성: } ``` -`voiceCall.enabled`의 기본값은 `true`입니다. Twilio 전송 방식을 사용하면 -실제 PSTN 통화, DTMF, 소개 인사말을 Voice Call Plugin에 위임합니다. Voice -Call은 실시간 미디어 스트림을 열기 전에 DTMF 시퀀스를 재생한 다음, 저장된 -소개 텍스트를 초기 실시간 인사말로 사용합니다. `voice-call`이 활성화되어 -있지 않으면 Google Meet은 여전히 다이얼 플랜을 검증하고 기록할 수 있지만 -Twilio 통화를 걸 수는 없습니다. +`voiceCall.enabled`의 기본값은 `true`입니다. Twilio 전송에서는 실제 PSTN 통화, +DTMF, 인트로 인사를 Voice Call Plugin에 위임합니다. Voice Call은 실시간 미디어 +스트림을 열기 전에 DTMF 시퀀스를 재생한 다음 저장된 인트로 텍스트를 초기 실시간 +인사로 사용합니다. `voice-call`이 활성화되어 있지 않으면 Google Meet는 여전히 +다이얼 플랜을 검증하고 기록할 수 있지만 Twilio 통화를 걸 수는 없습니다. ## 도구 @@ -1080,48 +1121,46 @@ Twilio 통화를 걸 수는 없습니다. } ``` -Chrome이 Gateway 호스트에서 실행될 때는 `transport: "chrome"`을 -사용하세요. Chrome이 Parallels VM 같은 페어링된 노드에서 실행될 때는 +Chrome이 Gateway 호스트에서 실행될 때는 `transport: "chrome"`을 사용하세요. +Chrome이 Parallels VM 같은 페어링된 노드에서 실행될 때는 `transport: "chrome-node"`를 사용하세요. 두 경우 모두 모델 제공자와 -`openclaw_agent_consult`는 Gateway 호스트에서 실행되므로 모델 자격 증명은 -그곳에 유지됩니다. 기본 `mode: "agent"`에서는 실시간 전사 제공자가 듣기를 -처리하고, 구성된 OpenClaw 에이전트가 답변을 생성하며, 일반 OpenClaw TTS가 -이를 Meet에 말합니다. 실시간 음성 모델이 직접 답변하도록 하려면 -`mode: "bidi"`를 사용하세요. 원시 `mode: "realtime"`은 `mode: "agent"`의 -레거시 호환 별칭으로 계속 허용되지만, 더 이상 에이전트 도구 스키마에 -노출되지 않습니다. 에이전트 모드 로그에는 브리지 시작 시 확인된 전사 -제공자/모델과, 각 합성 응답 후 TTS 제공자, 모델, 음성, 출력 형식, 샘플 -레이트가 포함됩니다. +`openclaw_agent_consult`는 Gateway 호스트에서 실행되므로 모델 자격 증명은 그곳에 +유지됩니다. 기본 `mode: "agent"`에서는 실시간 트랜스크립션 제공자가 듣기를 +처리하고, 구성된 OpenClaw 에이전트가 답변을 생성하며, 일반 OpenClaw TTS가 이를 +Meet에 말합니다. 실시간 음성 모델이 직접 답변하기를 원하면 `mode: "bidi"`를 +사용하세요. 원시 `mode: "realtime"`은 `mode: "agent"`의 레거시 호환성 별칭으로 +계속 허용되지만, 더 이상 에이전트 도구 스키마에 광고되지 않습니다. 에이전트 모드 +로그에는 브리지 시작 시 해결된 트랜스크립션 제공자/모델과 각 합성 응답 후 TTS +제공자, 모델, 음성, 출력 형식, 샘플 레이트가 포함됩니다. -활성 세션을 나열하거나 세션 ID를 검사하려면 `action: "status"`를 -사용하세요. 실시간 에이전트가 즉시 말하도록 하려면 `sessionId` 및 -`message`와 함께 `action: "speak"`를 사용하세요. 세션을 생성하거나 -재사용하고, 알려진 문구를 트리거하며, Chrome 호스트가 보고할 수 있을 때 -`inCall` 상태를 반환하려면 `action: "test_speech"`를 사용하세요. -`test_speech`는 항상 `mode: "agent"`를 강제하며, 관찰 전용 세션은 의도적으로 -음성을 내보낼 수 없으므로 `mode: "transcribe"`로 실행하라고 요청하면 -실패합니다. `speechOutputVerified` 결과는 이 테스트 호출 중 실시간 오디오 -출력 바이트가 증가했는지를 기준으로 하므로, 이전 오디오가 있는 재사용된 -세션은 새로운 성공적 음성 확인으로 간주되지 않습니다. 세션을 종료됨으로 -표시하려면 `action: "leave"`를 사용하세요. +활성 세션을 나열하거나 세션 ID를 검사하려면 `action: "status"`를 사용하세요. +실시간 에이전트가 즉시 말하게 하려면 `sessionId` 및 `message`와 함께 +`action: "speak"`를 사용하세요. 세션을 만들거나 재사용하고, 알려진 문구를 +트리거하며, Chrome 호스트가 보고할 수 있을 때 `inCall` 상태를 반환하려면 +`action: "test_speech"`를 사용하세요. `test_speech`는 항상 `mode: "agent"`를 +강제하며, 관찰 전용 세션은 의도적으로 음성을 내보낼 수 없기 때문에 +`mode: "transcribe"`로 실행하라고 요청하면 실패합니다. `speechOutputVerified` +결과는 이 테스트 호출 중 실시간 오디오 출력 바이트가 증가하는지를 기반으로 +하므로, 이전 오디오가 있는 재사용 세션은 새로운 성공적인 음성 확인으로 간주되지 +않습니다. 세션을 종료된 것으로 표시하려면 `action: "leave"`를 사용하세요. `status`에는 사용 가능한 경우 Chrome 상태가 포함됩니다. - `inCall`: Chrome이 Meet 통화 안에 있는 것으로 보입니다. -- `micMuted`: 최선 노력 방식의 Meet 마이크 상태 +- `micMuted`: 최선의 방식으로 확인한 Meet 마이크 상태 - `manualActionRequired` / `manualActionReason` / `manualActionMessage`: - 음성이 작동하기 전에 브라우저 프로필에 수동 로그인, Meet 호스트 승인, - 권한 또는 브라우저 제어 복구가 필요합니다. -- `speechReady` / `speechBlockedReason` / `speechBlockedMessage`: 관리형 - Chrome 음성이 지금 허용되는지 여부입니다. `speechReady: false`는 - OpenClaw가 소개/테스트 문구를 오디오 브리지로 보내지 않았음을 의미합니다. + 음성이 작동하기 전에 브라우저 프로필에 수동 로그인, Meet 호스트 승인, 권한 또는 + 브라우저 제어 복구가 필요합니다. +- `speechReady` / `speechBlockedReason` / `speechBlockedMessage`: 관리되는 + Chrome 음성이 현재 허용되는지 여부. `speechReady: false`는 OpenClaw가 인트로/테스트 + 문구를 오디오 브리지로 보내지 않았음을 의미합니다. - `providerConnected` / `realtimeReady`: 실시간 음성 브리지 상태 -- `lastInputAt` / `lastOutputAt`: 브리지에서 마지막으로 확인되었거나 - 브리지로 전송된 오디오 +- `lastInputAt` / `lastOutputAt`: 브리지에서 마지막으로 오디오를 보거나 브리지로 + 보낸 시간 - `audioOutputRouted` / `audioOutputDeviceLabel`: Meet 탭의 미디어 출력이 - 브리지에서 사용하는 BlackHole 장치로 능동적으로 라우팅되었는지 여부 -- `lastSuppressedInputAt` / `suppressedInputBytes`: 어시스턴트 재생이 활성 - 상태일 때 무시된 루프백 입력 + 브리지가 사용하는 BlackHole 장치로 능동적으로 라우팅되었는지 여부 +- `lastSuppressedInputAt` / `suppressedInputBytes`: 어시스턴트 재생이 활성 상태일 + 때 무시된 loopback 입력 ```json { @@ -1133,46 +1172,45 @@ Chrome이 Gateway 호스트에서 실행될 때는 `transport: "chrome"`을 ## 에이전트 및 Bidi 모드 -Chrome `agent` 모드는 "내 에이전트가 회의에 있음" 동작에 최적화되어 -있습니다. 실시간 전사 제공자가 회의 오디오를 듣고, 최종 참가자 전사는 -구성된 OpenClaw 에이전트로 라우팅되며, 답변은 일반 OpenClaw TTS 런타임을 -통해 음성으로 출력됩니다. 실시간 음성 모델이 직접 답변하도록 하려면 -`mode: "bidi"`를 설정하세요. 가까운 최종 전사 조각은 consult 전에 병합되어 -하나의 발화 차례가 여러 개의 오래된 부분 답변을 만들지 않도록 합니다. 또한 -대기 중인 어시스턴트 오디오가 아직 재생되는 동안에는 실시간 입력이 -억제되며, 최근 어시스턴트와 유사한 전사 에코는 에이전트 consult 전에 -무시되어 BlackHole 루프백으로 인해 에이전트가 자신의 말에 답변하지 않도록 -합니다. +Chrome `agent` 모드는 "내 에이전트가 회의에 있음" 동작에 최적화되어 있습니다. +실시간 트랜스크립션 제공자가 회의 오디오를 듣고, 최종 참여자 전사는 구성된 +OpenClaw 에이전트로 라우팅되며, 답변은 일반 OpenClaw TTS 런타임을 통해 +말해집니다. 실시간 음성 모델이 직접 답변하기를 원하면 `mode: "bidi"`를 +설정하세요. 가까운 최종 전사 조각은 consult 전에 병합되어 하나의 발화 차례가 +여러 개의 오래된 부분 답변을 생성하지 않도록 합니다. 또한 대기 중인 어시스턴트 +오디오가 아직 재생 중일 때는 실시간 입력이 억제되며, 최근 어시스턴트처럼 보이는 +전사 에코는 에이전트 consult 전에 무시되어 BlackHole loopback으로 인해 에이전트가 +자신의 발화에 답하지 않도록 합니다. -| 모드 | 답변을 결정하는 주체 | 음성 출력 경로 | 사용할 때 | +| 모드 | 답변을 결정하는 주체 | 음성 출력 경로 | 사용 시점 | | ------- | ----------------------------- | -------------------------------------- | ----------------------------------------------------- | | `agent` | 구성된 OpenClaw 에이전트 | 일반 OpenClaw TTS 런타임 | "내 에이전트가 회의에 있음" 동작을 원할 때 | -| `bidi` | 실시간 음성 모델 | 실시간 음성 제공자 오디오 응답 | 가장 낮은 지연 시간의 대화형 음성 루프를 원할 때 | +| `bidi` | 실시간 음성 모델 | 실시간 음성 제공자 오디오 응답 | 최저 지연 시간의 대화형 음성 루프를 원할 때 | -`bidi` 모드에서 실시간 모델에 더 깊은 추론, 최신 정보 또는 일반 OpenClaw -도구가 필요하면 `openclaw_agent_consult`를 호출할 수 있습니다. +`bidi` 모드에서 실시간 모델에 더 깊은 추론, 최신 정보 또는 일반 OpenClaw 도구가 +필요한 경우 `openclaw_agent_consult`를 호출할 수 있습니다. -consult 도구는 백그라운드에서 최근 회의 대화록 컨텍스트와 함께 일반 OpenClaw 에이전트를 실행하고 간결한 음성 답변을 반환합니다. `agent` 모드에서는 OpenClaw가 그 답변을 TTS 런타임으로 직접 보내며, `bidi` 모드에서는 실시간 음성 모델이 consult 결과를 회의에 다시 말할 수 있습니다. 이는 Voice Call과 동일한 공유 consult 메커니즘을 사용합니다. +consult 도구는 최근 회의 transcript 컨텍스트와 함께 일반 OpenClaw agent를 백그라운드에서 실행하고 간결한 음성 답변을 반환합니다. `agent` 모드에서는 OpenClaw가 해당 답변을 TTS 런타임으로 직접 전송하고, `bidi` 모드에서는 realtime 음성 모델이 consult 결과를 회의 안으로 다시 말할 수 있습니다. Voice Call과 동일한 공유 consult 메커니즘을 사용합니다. -기본적으로 consult는 `main` 에이전트에서 실행됩니다. Meet 레인이 전용 OpenClaw 에이전트 워크스페이스, 모델 기본값, 도구 정책, 메모리, 세션 기록을 consult해야 하는 경우 `realtime.agentId`를 설정하세요. +기본적으로 consult는 `main` agent를 대상으로 실행됩니다. Meet 레인이 전용 OpenClaw agent 워크스페이스, 모델 기본값, 도구 정책, 메모리, 세션 기록을 consult해야 하는 경우 `realtime.agentId`를 설정하세요. -에이전트 모드 consult는 회의별 `agent::subagent:google-meet:` 세션 키를 사용하므로 후속 질문은 구성된 에이전트의 일반 에이전트 정책을 상속하면서 회의 컨텍스트를 유지합니다. +Agent 모드 consult는 회의별 `agent::subagent:google-meet:` 세션 키를 사용하므로, 후속 질문은 구성된 agent의 일반 agent 정책을 상속하면서 회의 컨텍스트를 유지합니다. `realtime.toolPolicy`는 consult 실행을 제어합니다. -- `safe-read-only`: consult 도구를 노출하고 일반 에이전트를 `read`, `web_search`, `web_fetch`, `x_search`, `memory_search`, `memory_get`으로 제한합니다. -- `owner`: consult 도구를 노출하고 일반 에이전트가 일반 에이전트 도구 정책을 사용하도록 허용합니다. -- `none`: 실시간 음성 모델에 consult 도구를 노출하지 않습니다. +- `safe-read-only`: consult 도구를 노출하고 일반 agent를 `read`, `web_search`, `web_fetch`, `x_search`, `memory_search`, `memory_get`으로 제한합니다. +- `owner`: consult 도구를 노출하고 일반 agent가 일반 agent 도구 정책을 사용하도록 허용합니다. +- `none`: realtime 음성 모델에 consult 도구를 노출하지 않습니다. consult 세션 키는 Meet 세션별로 범위가 지정되므로 후속 consult 호출은 같은 회의 중 이전 consult 컨텍스트를 재사용할 수 있습니다. -Chrome이 통화에 완전히 참가한 후 음성 준비 상태 확인을 강제하려면: +Chrome이 통화에 완전히 참여한 뒤 음성 준비 상태 확인을 강제로 실행하려면: ```bash openclaw googlemeet speak meet_... "Say exactly: I'm here and listening." ``` -전체 참가 및 말하기 스모크 테스트는 다음과 같습니다. +전체 참여 및 말하기 smoke는 다음과 같습니다. ```bash openclaw googlemeet test-speech https://meet.google.com/abc-defg-hij \ @@ -1180,9 +1218,9 @@ openclaw googlemeet test-speech https://meet.google.com/abc-defg-hij \ --message "Say exactly: I'm here and listening." ``` -## 라이브 테스트 체크리스트 +## Live 테스트 체크리스트 -무인 에이전트에 회의를 넘기기 전에 이 순서를 사용하세요. +무인 agent에 회의를 넘기기 전에 다음 순서를 사용하세요. ```bash openclaw googlemeet setup @@ -1194,13 +1232,13 @@ openclaw googlemeet test-speech https://meet.google.com/abc-defg-hij \ 예상 Chrome-node 상태: -- `googlemeet setup`이 모두 초록색입니다. -- Chrome-node가 기본 전송 방식이거나 노드가 고정된 경우 `googlemeet setup`에 `chrome-node-connected`가 포함됩니다. -- `nodes status`에 선택한 노드가 연결된 것으로 표시됩니다. -- 선택한 노드가 `googlemeet.chrome`과 `browser.proxy`를 모두 알립니다. -- Meet 탭이 통화에 참가하고 `test-speech`가 `inCall: true`와 함께 Chrome 상태를 반환합니다. +- `googlemeet setup`이 모두 녹색입니다. +- Chrome-node가 기본 transport이거나 node가 고정된 경우 `googlemeet setup`에 `chrome-node-connected`가 포함됩니다. +- `nodes status`에 선택한 node가 연결된 것으로 표시됩니다. +- 선택한 node가 `googlemeet.chrome`과 `browser.proxy`를 모두 알립니다. +- Meet 탭이 통화에 참여하고 `test-speech`가 `inCall: true`인 Chrome 상태를 반환합니다. -Parallels macOS VM 같은 원격 Chrome 호스트의 경우, Gateway 또는 VM을 업데이트한 뒤 다음이 가장 짧은 안전 확인입니다. +Parallels macOS VM 같은 원격 Chrome 호스트의 경우, Gateway 또는 VM을 업데이트한 뒤 가장 짧은 안전 확인은 다음과 같습니다. ```bash openclaw googlemeet setup @@ -1211,9 +1249,9 @@ openclaw nodes invoke \ --params '{"action":"setup"}' ``` -이는 에이전트가 실제 회의 탭을 열기 전에 Gateway Plugin이 로드되었고, VM 노드가 현재 토큰으로 연결되었으며, Meet 오디오 브리지를 사용할 수 있음을 증명합니다. +이는 agent가 실제 회의 탭을 열기 전에 Gateway Plugin이 로드되었고, VM node가 현재 토큰으로 연결되었으며, Meet 오디오 브리지를 사용할 수 있음을 증명합니다. -Twilio 스모크 테스트에는 전화 다이얼인 세부 정보를 노출하는 회의를 사용하세요. +Twilio smoke의 경우 전화 접속 세부 정보를 노출하는 회의를 사용하세요. ```bash openclaw googlemeet setup @@ -1225,30 +1263,30 @@ openclaw googlemeet join https://meet.google.com/abc-defg-hij \ 예상 Twilio 상태: -- `googlemeet setup`에 초록색 `twilio-voice-call-plugin`, `twilio-voice-call-credentials`, `twilio-voice-call-webhook` 확인이 포함됩니다. -- Gateway 다시 로드 후 CLI에서 `voicecall`을 사용할 수 있습니다. +- `googlemeet setup`에 녹색 `twilio-voice-call-plugin`, `twilio-voice-call-credentials`, `twilio-voice-call-webhook` 확인이 포함됩니다. +- Gateway reload 후 CLI에서 `voicecall`을 사용할 수 있습니다. - 반환된 세션에 `transport: "twilio"`와 `twilio.voiceCallId`가 있습니다. -- `openclaw logs --follow`에 실시간 TwiML 전에 DTMF TwiML이 제공된 뒤, 초기 인사말이 대기열에 들어간 실시간 브리지가 표시됩니다. -- `googlemeet leave `가 위임된 음성 통화를 끊습니다. +- `openclaw logs --follow`는 realtime TwiML 전에 DTMF TwiML이 제공된 뒤, 초기 인사말이 대기열에 들어간 realtime 브리지를 표시합니다. +- `googlemeet leave `가 위임된 음성 통화를 종료합니다. ## 문제 해결 -### 에이전트가 Google Meet 도구를 볼 수 없음 +### Agent가 Google Meet 도구를 볼 수 없음 -Gateway 구성에서 Plugin이 활성화되었는지 확인하고 Gateway를 다시 로드하세요. +Gateway 구성에서 Plugin이 활성화되어 있는지 확인하고 Gateway를 reload하세요. ```bash openclaw plugins list | grep google-meet openclaw googlemeet setup ``` -방금 `plugins.entries.google-meet`를 편집했다면 Gateway를 다시 시작하거나 다시 로드하세요. 실행 중인 에이전트는 현재 Gateway 프로세스가 등록한 Plugin 도구만 볼 수 있습니다. +방금 `plugins.entries.google-meet`를 편집했다면 Gateway를 재시작하거나 reload하세요. 실행 중인 agent는 현재 Gateway 프로세스가 등록한 Plugin 도구만 볼 수 있습니다. -macOS가 아닌 Gateway 호스트에서는 에이전트용 `google_meet` 도구가 계속 표시되지만, 로컬 Chrome talk-back 작업은 오디오 브리지에 도달하기 전에 차단됩니다. 로컬 Chrome talk-back 오디오는 현재 macOS `BlackHole 2ch`에 의존하므로 Linux 에이전트는 기본 로컬 Chrome 에이전트 경로 대신 `mode: "transcribe"`, Twilio 다이얼인 또는 macOS `chrome-node` 호스트를 사용해야 합니다. +macOS가 아닌 Gateway 호스트에서는 agent용 `google_meet` 도구가 계속 표시되지만, 로컬 Chrome talk-back 작업은 오디오 브리지에 도달하기 전에 차단됩니다. 로컬 Chrome talk-back 오디오는 현재 macOS `BlackHole 2ch`에 의존하므로 Linux agent는 기본 로컬 Chrome agent 경로 대신 `mode: "transcribe"`, Twilio 전화 접속 또는 macOS `chrome-node` 호스트를 사용해야 합니다. -### 연결된 Google Meet 지원 노드 없음 +### 연결된 Google Meet 가능 node가 없음 -노드 호스트에서 실행하세요. +node 호스트에서 다음을 실행하세요. ```bash openclaw plugins enable google-meet @@ -1257,7 +1295,7 @@ OPENCLAW_ALLOW_INSECURE_PRIVATE_WS=1 \ openclaw node run --host --port 18789 --display-name parallels-macos ``` -Gateway 호스트에서 노드를 승인하고 명령을 확인하세요. +Gateway 호스트에서 node를 승인하고 명령을 확인하세요. ```bash openclaw devices list @@ -1265,7 +1303,7 @@ openclaw devices approve openclaw nodes status ``` -노드는 연결되어 있어야 하며 `googlemeet.chrome` 및 `browser.proxy`를 나열해야 합니다. Gateway 구성은 해당 노드 명령을 허용해야 합니다. +node는 연결되어 있어야 하며 `googlemeet.chrome`과 `browser.proxy`를 함께 나열해야 합니다. Gateway 구성은 해당 node 명령을 허용해야 합니다. ```json5 { @@ -1277,7 +1315,7 @@ openclaw nodes status } ``` -`googlemeet setup`에서 `chrome-node-connected`가 실패하거나 Gateway 로그가 `gateway token mismatch`를 보고하면, 현재 Gateway 토큰으로 노드를 다시 설치하거나 다시 시작하세요. LAN Gateway의 경우 이는 보통 다음을 의미합니다. +`googlemeet setup`이 `chrome-node-connected`에서 실패하거나 Gateway 로그가 `gateway token mismatch`를 보고하는 경우, 현재 Gateway 토큰으로 node를 다시 설치하거나 재시작하세요. LAN Gateway의 경우 일반적으로 다음을 의미합니다. ```bash OPENCLAW_ALLOW_INSECURE_PRIVATE_WS=1 \ @@ -1288,74 +1326,74 @@ OPENCLAW_ALLOW_INSECURE_PRIVATE_WS=1 \ --force ``` -그런 다음 노드 서비스를 다시 로드하고 다시 실행하세요. +그런 다음 node 서비스를 reload하고 다시 실행하세요. ```bash openclaw googlemeet setup openclaw nodes status --connected ``` -### 브라우저는 열리지만 에이전트가 참가할 수 없음 +### 브라우저는 열리지만 agent가 참여할 수 없음 -관찰 전용 참가에는 `googlemeet test-listen`을, 실시간 참가에는 `googlemeet test-speech`를 실행한 다음 반환된 Chrome 상태를 검사하세요. 어느 프로브든 `manualActionRequired: true`를 보고하면 운영자에게 `manualActionMessage`를 보여주고 브라우저 작업이 완료될 때까지 재시도를 중지하세요. +관찰 전용 참여에는 `googlemeet test-listen`을, realtime 참여에는 `googlemeet test-speech`를 실행한 다음 반환된 Chrome 상태를 검사하세요. 두 probe 중 하나가 `manualActionRequired: true`를 보고하면 운영자에게 `manualActionMessage`를 보여주고 브라우저 작업이 완료될 때까지 재시도를 중지하세요. 일반적인 수동 작업: - Chrome 프로필에 로그인합니다. - Meet 호스트 계정에서 게스트를 승인합니다. - Chrome의 네이티브 권한 프롬프트가 나타나면 Chrome 마이크/카메라 권한을 부여합니다. -- 멈춘 Meet 권한 대화 상자를 닫거나 복구합니다. +- 멈춘 Meet 권한 대화상자를 닫거나 복구합니다. -Meet에 "Do you want people to hear you in the meeting?"가 표시된다는 이유만으로 "not signed in"을 보고하지 마세요. 이는 Meet의 오디오 선택 중간 화면입니다. OpenClaw는 사용 가능한 경우 브라우저 자동화를 통해 **Use microphone**을 클릭하고 실제 회의 상태를 계속 기다립니다. 생성 전용 브라우저 fallback의 경우 URL 생성에는 실시간 오디오 경로가 필요하지 않으므로 OpenClaw가 **Continue without microphone**을 클릭할 수 있습니다. +Meet가 "Do you want people to hear you in the meeting?"를 표시한다고 해서 "로그인되지 않음"이라고 보고하지 마세요. 이는 Meet의 오디오 선택 중간 화면입니다. OpenClaw는 사용할 수 있는 경우 브라우저 자동화를 통해 **Use microphone**을 클릭하고 실제 회의 상태를 계속 기다립니다. 생성 전용 브라우저 fallback의 경우 URL 생성에는 realtime 오디오 경로가 필요하지 않으므로 OpenClaw가 **Continue without microphone**을 클릭할 수 있습니다. ### 회의 생성 실패 -`googlemeet create`는 OAuth 자격 증명이 구성된 경우 먼저 Google Meet API `spaces.create` 엔드포인트를 사용합니다. OAuth 자격 증명이 없으면 고정된 Chrome 노드 브라우저로 fallback합니다. 다음을 확인하세요. +OAuth 자격 증명이 구성된 경우 `googlemeet create`는 먼저 Google Meet API `spaces.create` endpoint를 사용합니다. OAuth 자격 증명이 없으면 고정된 Chrome node 브라우저로 fallback합니다. 다음을 확인하세요. - API 생성: `oauth.clientId`와 `oauth.refreshToken`이 구성되어 있거나, 일치하는 `OPENCLAW_GOOGLE_MEET_*` 환경 변수가 있어야 합니다. -- API 생성: 생성 지원이 추가된 뒤 refresh token이 발급되었어야 합니다. 오래된 토큰에는 `meetings.space.created` scope가 없을 수 있습니다. `openclaw googlemeet auth login --json`을 다시 실행하고 Plugin 구성을 업데이트하세요. -- 브라우저 fallback: `defaultTransport: "chrome-node"` 및 `chromeNode.node`가 `browser.proxy`와 `googlemeet.chrome`을 가진 연결된 노드를 가리켜야 합니다. -- 브라우저 fallback: 해당 노드의 OpenClaw Chrome 프로필이 Google에 로그인되어 있고 `https://meet.google.com/new`를 열 수 있어야 합니다. -- 브라우저 fallback: 재시도는 새 탭을 열기 전에 기존 `https://meet.google.com/new` 또는 Google 계정 프롬프트 탭을 재사용합니다. 에이전트가 시간 초과되면 다른 Meet 탭을 수동으로 열지 말고 도구 호출을 재시도하세요. -- 브라우저 fallback: 도구가 `manualActionRequired: true`를 반환하면 반환된 `browser.nodeId`, `browser.targetId`, `browserUrl`, `manualActionMessage`를 사용해 운영자를 안내하세요. 해당 작업이 완료될 때까지 루프로 재시도하지 마세요. -- 브라우저 fallback: Meet에 "Do you want people to hear you in the meeting?"가 표시되면 탭을 열어 둡니다. OpenClaw는 브라우저 자동화를 통해 **Use microphone** 또는 생성 전용 fallback의 경우 **Continue without microphone**을 클릭하고 생성된 Meet URL을 계속 기다려야 합니다. 할 수 없는 경우 오류는 `google-login-required`가 아니라 `meet-audio-choice-required`를 언급해야 합니다. +- API 생성: refresh token은 생성 지원이 추가된 이후에 발급된 것이어야 합니다. 오래된 토큰에는 `meetings.space.created` scope가 없을 수 있습니다. `openclaw googlemeet auth login --json`을 다시 실행하고 Plugin 구성을 업데이트하세요. +- 브라우저 fallback: `defaultTransport: "chrome-node"`이고 `chromeNode.node`가 `browser.proxy` 및 `googlemeet.chrome`이 있는 연결된 node를 가리킵니다. +- 브라우저 fallback: 해당 node의 OpenClaw Chrome 프로필이 Google에 로그인되어 있고 `https://meet.google.com/new`를 열 수 있습니다. +- 브라우저 fallback: 재시도는 새 탭을 열기 전에 기존 `https://meet.google.com/new` 또는 Google 계정 프롬프트 탭을 재사용합니다. agent가 시간 초과되면 다른 Meet 탭을 수동으로 여는 대신 도구 호출을 재시도하세요. +- 브라우저 fallback: 도구가 `manualActionRequired: true`를 반환하면 반환된 `browser.nodeId`, `browser.targetId`, `browserUrl`, `manualActionMessage`를 사용해 운영자를 안내하세요. 해당 작업이 완료될 때까지 반복해서 재시도하지 마세요. +- 브라우저 fallback: Meet가 "Do you want people to hear you in the meeting?"를 표시하면 탭을 열어 둡니다. OpenClaw는 브라우저 자동화를 통해 **Use microphone** 또는 생성 전용 fallback의 경우 **Continue without microphone**을 클릭하고 생성된 Meet URL을 계속 기다려야 합니다. 그렇게 할 수 없는 경우 오류는 `google-login-required`가 아니라 `meet-audio-choice-required`를 언급해야 합니다. -### 에이전트가 참가하지만 말하지 않음 +### Agent가 참여하지만 말하지 않음 -실시간 경로를 확인하세요. +realtime 경로를 확인하세요. ```bash openclaw googlemeet setup openclaw googlemeet doctor ``` -일반 STT -> OpenClaw 에이전트 -> TTS talk-back 경로에는 `mode: "agent"`를 사용하고, 직접 실시간 음성 fallback에는 `mode: "bidi"`를 사용하세요. `mode: "transcribe"`는 의도적으로 talk-back 브리지를 시작하지 않습니다. 관찰 전용 디버깅의 경우 참가자가 말한 뒤 `openclaw googlemeet status --json `를 실행하고 `captioning`, `transcriptLines`, `lastCaptionText`를 확인하세요. `inCall`이 true이지만 `transcriptLines`가 `0`에 머무르면 Meet 자막이 비활성화되었거나, 관찰자가 설치된 이후 아무도 말하지 않았거나, Meet UI가 변경되었거나, 회의 언어/계정에서 실시간 자막을 사용할 수 없을 수 있습니다. +일반 STT -> OpenClaw agent -> TTS talk-back 경로에는 `mode: "agent"`를 사용하고, 직접 realtime 음성 fallback에는 `mode: "bidi"`를 사용하세요. `mode: "transcribe"`는 의도적으로 talk-back 브리지를 시작하지 않습니다. 관찰 전용 디버깅의 경우 참가자가 말한 뒤 `openclaw googlemeet status --json `를 실행하고 `captioning`, `transcriptLines`, `lastCaptionText`를 확인하세요. `inCall`은 true지만 `transcriptLines`가 `0`에 머물러 있다면 Meet caption이 비활성화되어 있거나, 관찰자가 설치된 이후 아무도 말하지 않았거나, Meet UI가 변경되었거나, 회의 언어/계정에서 live caption을 사용할 수 없을 수 있습니다. -`googlemeet test-speech`는 항상 실시간 경로를 확인하고 해당 호출에서 브리지 출력 바이트가 관찰되었는지 보고합니다. `speechOutputVerified`가 false이고 `speechOutputTimedOut`이 true이면 실시간 제공자가 발화를 수락했을 수 있지만, OpenClaw가 새 출력 바이트가 Chrome 오디오 브리지에 도달하는 것을 보지 못한 것입니다. +`googlemeet test-speech`는 항상 realtime 경로를 확인하고 해당 호출에서 브리지 출력 바이트가 관찰되었는지 보고합니다. `speechOutputVerified`가 false이고 `speechOutputTimedOut`이 true인 경우 realtime provider가 발화를 수락했을 수 있지만 OpenClaw가 새 출력 바이트가 Chrome 오디오 브리지에 도달하는 것을 보지 못한 것입니다. -또한 다음을 확인하세요. +다음도 확인하세요. -- Gateway 호스트에서 `OPENAI_API_KEY` 또는 `GEMINI_API_KEY` 같은 실시간 제공자 키를 사용할 수 있습니다. -- Chrome 호스트에서 `BlackHole 2ch`가 보입니다. +- Gateway 호스트에서 `OPENAI_API_KEY` 또는 `GEMINI_API_KEY` 같은 realtime provider 키를 사용할 수 있습니다. +- Chrome 호스트에서 `BlackHole 2ch`가 표시됩니다. - Chrome 호스트에 `sox`가 있습니다. -- Meet 마이크와 스피커가 OpenClaw가 사용하는 가상 오디오 경로를 통해 라우팅됩니다. 로컬 Chrome 실시간 참가의 경우 `doctor`에 `meet output routed: yes`가 표시되어야 합니다. +- Meet 마이크와 스피커가 OpenClaw가 사용하는 가상 오디오 경로를 통해 라우팅됩니다. 로컬 Chrome realtime 참여의 경우 `doctor`에 `meet output routed: yes`가 표시되어야 합니다. -`googlemeet doctor [session-id]`는 세션, 노드, 통화 중 상태, 수동 작업 이유, 실시간 제공자 연결, `realtimeReady`, 오디오 입력/출력 활동, 마지막 오디오 타임스탬프, 바이트 카운터, 브라우저 URL을 출력합니다. 원시 JSON이 필요하면 `googlemeet status [session-id] --json`을 사용하세요. 토큰을 노출하지 않고 Google Meet OAuth refresh를 확인해야 하면 `googlemeet doctor --oauth`를 사용하세요. Google Meet API 증명도 필요하면 `--meeting` 또는 `--create-space`를 추가하세요. +`googlemeet doctor [session-id]`는 세션, node, 통화 중 상태, 수동 작업 이유, realtime provider 연결, `realtimeReady`, 오디오 입력/출력 활동, 마지막 오디오 타임스탬프, 바이트 카운터, 브라우저 URL을 출력합니다. 원시 JSON이 필요하면 `googlemeet status [session-id] --json`을 사용하세요. 토큰을 노출하지 않고 Google Meet OAuth refresh를 확인해야 하는 경우 `googlemeet doctor --oauth`를 사용하고, Google Meet API 증명도 필요하면 `--meeting` 또는 `--create-space`를 추가하세요. -에이전트가 시간 초과되었고 이미 열린 Meet 탭이 보인다면, 다른 탭을 열지 말고 해당 탭을 검사하세요. +agent가 시간 초과되었고 이미 열린 Meet 탭이 보이면 다른 탭을 열지 말고 그 탭을 검사하세요. ```bash openclaw googlemeet recover-tab openclaw googlemeet recover-tab https://meet.google.com/abc-defg-hij ``` -동등한 도구 작업은 `recover_current_tab`입니다. 선택한 전송 방식의 기존 Meet 탭에 포커스를 맞추고 검사합니다. `chrome`에서는 Gateway를 통한 로컬 브라우저 제어를 사용하고, `chrome-node`에서는 구성된 Chrome 노드를 사용합니다. 새 탭을 열거나 새 세션을 생성하지 않습니다. 로그인, 승인, 권한 또는 오디오 선택 상태 같은 현재 차단 요인을 보고합니다. CLI 명령은 구성된 Gateway와 통신하므로 Gateway가 실행 중이어야 합니다. `chrome-node`에는 Chrome 노드도 연결되어 있어야 합니다. +동등한 도구 작업은 `recover_current_tab`입니다. 선택한 transport의 기존 Meet 탭에 focus하고 검사합니다. `chrome`에서는 Gateway를 통한 로컬 브라우저 제어를 사용하고, `chrome-node`에서는 구성된 Chrome node를 사용합니다. 새 탭을 열거나 새 세션을 만들지 않습니다. 로그인, 승인, 권한 또는 오디오 선택 상태 같은 현재 blocker를 보고합니다. CLI 명령은 구성된 Gateway와 통신하므로 Gateway가 실행 중이어야 합니다. `chrome-node`에는 Chrome node 연결도 필요합니다. ### Twilio 설정 확인 실패 -`voice-call`이 허용되지 않았거나 활성화되지 않은 경우 `twilio-voice-call-plugin`이 실패합니다. `plugins.allow`에 추가하고 `plugins.entries.voice-call`을 활성화한 뒤 Gateway를 다시 로드하세요. +`voice-call`이 허용되지 않았거나 활성화되지 않은 경우 `twilio-voice-call-plugin`이 실패합니다. `plugins.allow`에 추가하고 `plugins.entries.voice-call`을 활성화한 다음 Gateway를 reload하세요. -Twilio 백엔드에 계정 SID, 인증 토큰 또는 발신자 번호가 없으면 `twilio-voice-call-credentials`가 실패합니다. Gateway 호스트에서 다음을 설정하세요. +Twilio backend에 account SID, auth token 또는 caller number가 없으면 `twilio-voice-call-credentials`가 실패합니다. Gateway 호스트에서 다음을 설정하세요. ```bash export TWILIO_ACCOUNT_SID=AC... @@ -1363,9 +1401,9 @@ export TWILIO_AUTH_TOKEN=... export TWILIO_FROM_NUMBER=+15550001234 ``` -`voice-call`에 공개 Webhook 노출이 없거나 `publicUrl`이 loopback 또는 사설 네트워크 공간을 가리키면 `twilio-voice-call-webhook`이 실패합니다. `plugins.entries.voice-call.config.publicUrl`을 공개 제공자 URL로 설정하거나 `voice-call` 터널/Tailscale 노출을 구성하세요. +`voice-call`에 공개 Webhook 노출이 없거나 `publicUrl`이 loopback 또는 사설 네트워크 공간을 가리키면 `twilio-voice-call-webhook`이 실패합니다. `plugins.entries.voice-call.config.publicUrl`을 공개 provider URL로 설정하거나 `voice-call` 터널/Tailscale 노출을 구성하세요. -Loopback 및 사설 URL은 통신사 콜백에 유효하지 않습니다. `publicUrl`로 `localhost`, `127.0.0.1`, `0.0.0.0`, `10.x`, `172.16.x`-`172.31.x`, `192.168.x`, `169.254.x`, `fc00::/7`, `fd00::/8`을 사용하지 마세요. +Loopback 및 사설 URL은 통신사 callback에 유효하지 않습니다. `localhost`, `127.0.0.1`, `0.0.0.0`, `10.x`, `172.16.x`-`172.31.x`, `192.168.x`, `169.254.x`, `fc00::/7`, `fd00::/8`을 `publicUrl`로 사용하지 마세요. 안정적인 공개 URL의 경우: @@ -1412,21 +1450,22 @@ openclaw voicecall setup openclaw voicecall smoke ``` -`voicecall smoke`는 기본적으로 준비 상태만 확인합니다. 특정 번호로 드라이런하려면: +`voicecall smoke`는 기본적으로 준비 상태 확인 전용입니다. 특정 번호에 대해 모의 실행하려면 다음을 사용하세요. ```bash openclaw voicecall smoke --to "+15555550123" ``` -실제 아웃바운드 알림 전화를 의도적으로 걸고 싶을 때만 `--yes`를 추가하세요. +의도적으로 실제 아웃바운드 알림 전화를 걸려는 경우에만 `--yes`를 추가하세요. ```bash openclaw voicecall smoke --to "+15555550123" --yes ``` -### Twilio 통화가 시작되지만 회의에 들어가지 못함 +### Twilio 통화가 시작되지만 회의에 들어가지 않음 -Meet 이벤트가 전화 다이얼인 세부 정보를 노출하는지 확인하세요. 정확한 다이얼인 번호와 PIN 또는 사용자 지정 DTMF 시퀀스를 전달하세요. +Meet 이벤트가 전화 접속 세부 정보를 노출하는지 확인하세요. 정확한 전화 접속 +번호와 PIN 또는 사용자 지정 DTMF 시퀀스를 전달하세요. ```bash openclaw googlemeet join https://meet.google.com/abc-defg-hij \ @@ -1435,38 +1474,60 @@ openclaw googlemeet join https://meet.google.com/abc-defg-hij \ --dtmf-sequence ww123456# ``` -제공자가 PIN 입력 전에 일시 중지가 필요하면 `--dtmf-sequence`에서 선행 `w` 또는 쉼표를 사용하세요. +제공자가 PIN을 입력하기 전에 일시 정지가 필요하다면 `--dtmf-sequence`에서 +앞쪽 `w` 또는 쉼표를 사용하세요. -전화 통화가 생성되었지만 Meet 명단에 다이얼인 참가자가 표시되지 않는 경우: +전화 통화는 생성되었지만 Meet 명단에 전화 접속 참가자가 표시되지 않는 경우: -- `openclaw googlemeet doctor `를 실행하여 위임된 Twilio 통화 ID, DTMF가 대기열에 들어갔는지 여부, 소개 인사말이 요청되었는지 여부를 확인하세요. +- 위임된 Twilio 통화 ID, DTMF가 대기열에 추가되었는지 여부, 소개 인사말이 요청되었는지 여부를 확인하려면 `openclaw googlemeet doctor `를 실행하세요. - `openclaw voicecall status --call-id `를 실행하고 통화가 아직 활성 상태인지 확인하세요. - `openclaw voicecall tail`을 실행하고 Twilio Webhook이 Gateway에 도착하는지 확인하세요. -- `openclaw logs --follow`를 실행하고 Twilio Meet 시퀀스를 찾으세요. Google Meet가 참여를 위임하고, Voice Call이 전화 구간을 시작하고, Google Meet가 `voiceCall.dtmfDelayMs`만큼 기다린 뒤 `voicecall.dtmf`로 DTMF를 보내고, `voiceCall.postDtmfSpeechDelayMs`만큼 기다린 다음 `voicecall.speak`로 소개 음성을 요청합니다. +- `openclaw logs --follow`를 실행하고 Twilio Meet 시퀀스를 찾으세요. Google Meet이 참여를 위임하고, Voice Call이 전화 레그를 시작하고, Google Meet이 `voiceCall.dtmfDelayMs` 동안 대기한 다음 `voicecall.dtmf`로 DTMF를 보내고, `voiceCall.postDtmfSpeechDelayMs` 동안 대기한 뒤 `voicecall.speak`로 소개 음성을 요청합니다. - `openclaw googlemeet setup --transport twilio`를 다시 실행하세요. 녹색 설정 확인은 필수이지만 회의 PIN 시퀀스가 올바르다는 것을 증명하지는 않습니다. -- 다이얼인 번호가 PIN과 같은 Meet 초대 및 지역에 속하는지 확인하세요. +- 전화 접속 번호가 PIN과 동일한 Meet 초대 및 지역에 속하는지 확인하세요. - Meet 응답이 느리거나 DTMF가 전송된 후에도 통화 기록에 PIN을 요청하는 프롬프트가 계속 표시되면 `voiceCall.dtmfDelayMs`를 늘리세요. -- 참가자가 참여했지만 인사말이 들리지 않으면 `openclaw logs --follow`에서 DTMF 이후 `voicecall.speak` 요청과 미디어 스트림 TTS 재생 또는 Twilio `` 대체 경로를 확인하세요. 통화 기록에 여전히 "enter the meeting PIN"이 포함되어 있으면 전화 구간이 아직 Meet 회의실에 참여하지 않은 것이므로 회의 참가자는 음성을 들을 수 없습니다. +- 참가자가 참여했지만 인사말이 들리지 않는다면 `openclaw logs --follow`에서 DTMF 이후 `voicecall.speak` 요청과 미디어 스트림 TTS 재생 또는 Twilio `` 대체 동작을 확인하세요. 통화 기록에 "enter the meeting PIN"이 계속 포함되어 있다면 전화 레그가 아직 Meet 방에 참여하지 않은 것이므로 회의 참가자는 음성을 들을 수 없습니다. -Webhook이 도착하지 않으면 먼저 Voice Call Plugin을 디버그하세요. 제공자는 `plugins.entries.voice-call.config.publicUrl` 또는 구성된 터널에 도달할 수 있어야 합니다. [음성 통화 문제 해결](/ko/plugins/voice-call#troubleshooting)을 참조하세요. +Webhook이 도착하지 않는다면 먼저 Voice Call Plugin을 디버그하세요. 제공자가 +`plugins.entries.voice-call.config.publicUrl` 또는 구성된 터널에 도달할 수 있어야 합니다. +[음성 통화 문제 해결](/ko/plugins/voice-call#troubleshooting)을 참고하세요. ## 참고 -Google Meet의 공식 미디어 API는 수신 중심이므로 Meet 통화에서 말하려면 여전히 참가자 경로가 필요합니다. 이 Plugin은 그 경계를 명확히 유지합니다. Chrome은 브라우저 참여와 로컬 오디오 라우팅을 처리하고, Twilio는 전화 다이얼인 참여를 처리합니다. +Google Meet의 공식 미디어 API는 수신 중심이므로 Meet 통화에서 말하려면 여전히 +참가자 경로가 필요합니다. 이 Plugin은 그 경계를 명확하게 유지합니다. +Chrome은 브라우저 참여와 로컬 오디오 라우팅을 처리하고, Twilio는 전화 접속 +참여를 처리합니다. Chrome 토크백 모드에는 `BlackHole 2ch`와 다음 중 하나가 필요합니다. -- `chrome.audioInputCommand`와 `chrome.audioOutputCommand`: OpenClaw가 브리지를 소유하고, 해당 명령과 선택된 제공자 사이에서 `chrome.audioFormat`의 오디오를 파이프합니다. 에이전트 모드는 실시간 전사와 일반 TTS를 사용하고, bidi 모드는 실시간 음성 제공자를 사용합니다. 기본 Chrome 경로는 `chrome.audioBufferBytes: 4096`을 사용하는 24 kHz PCM16입니다. 8 kHz G.711 mu-law는 레거시 명령 쌍을 위해 계속 사용할 수 있습니다. -- `chrome.audioBridgeCommand`: 외부 브리지 명령이 전체 로컬 오디오 경로를 소유하며 데몬을 시작하거나 검증한 뒤 종료해야 합니다. `agent` 모드는 TTS를 위해 직접 명령 쌍 액세스가 필요하므로 이는 `bidi`에만 유효합니다. +- `chrome.audioInputCommand`와 `chrome.audioOutputCommand`: OpenClaw가 브리지를 소유하고 해당 명령과 선택한 제공자 사이에서 `chrome.audioFormat`의 오디오를 파이프로 전달합니다. 에이전트 모드는 실시간 전사와 일반 TTS를 사용하고, 양방향 모드는 실시간 음성 제공자를 사용합니다. 기본 Chrome 경로는 `chrome.audioBufferBytes: 4096`을 사용하는 24 kHz PCM16입니다. 8 kHz G.711 mu-law는 레거시 명령 쌍에서 계속 사용할 수 있습니다. +- `chrome.audioBridgeCommand`: 외부 브리지 명령이 전체 로컬 오디오 경로를 소유하며 데몬을 시작하거나 검증한 뒤 종료해야 합니다. `agent` 모드에는 TTS를 위한 직접 명령 쌍 액세스가 필요하므로 이는 `bidi`에만 유효합니다. -깔끔한 양방향 오디오를 위해 Meet 출력과 Meet 마이크를 별도의 가상 장치나 Loopback 스타일 가상 장치 그래프로 라우팅하세요. 하나의 공유 BlackHole 장치는 다른 참가자의 소리를 통화로 다시 에코할 수 있습니다. +에이전트가 에이전트 모드에서 `google_meet` 도구를 호출하면 회의 컨설턴트 +세션은 참가자 음성에 답하기 전에 호출자의 현재 기록을 포크합니다. Meet 세션은 +여전히 별도로 유지되므로(`agent::subagent:google-meet:`) +회의 후속 작업이 호출자 기록을 직접 변경하지 않습니다. -명령 쌍 Chrome 브리지를 사용할 때 `chrome.bargeInInputCommand`는 별도의 로컬 마이크를 수신하고 사람이 말하기 시작하면 어시스턴트 재생을 지울 수 있습니다. 이렇게 하면 공유 BlackHole loopback 입력이 어시스턴트 재생 중에 일시적으로 억제되더라도 사람의 음성이 어시스턴트 출력보다 앞서 유지됩니다. `chrome.audioInputCommand` 및 `chrome.audioOutputCommand`와 마찬가지로 이는 운영자가 구성하는 로컬 명령입니다. 명시적으로 신뢰할 수 있는 명령 경로나 인수 목록을 사용하고, 신뢰할 수 없는 위치의 스크립트를 가리키지 마세요. +깨끗한 양방향 오디오를 위해 Meet 출력과 Meet 마이크를 별도의 가상 장치 또는 +Loopback 스타일 가상 장치 그래프로 라우팅하세요. 단일 공유 BlackHole 장치는 +다른 참가자의 소리를 다시 통화로 반향시킬 수 있습니다. -`googlemeet speak`는 Chrome 세션에 대해 활성 토크백 오디오 브리지를 트리거합니다. `googlemeet leave`는 해당 브리지를 중지합니다. Voice Call Plugin을 통해 위임된 Twilio 세션의 경우 `leave`는 기반 음성 통화도 끊습니다. API로 관리되는 공간의 활성 Google Meet 회의도 닫으려면 `googlemeet end-active-conference`를 사용하세요. +명령 쌍 Chrome 브리지에서는 `chrome.bargeInInputCommand`가 별도 로컬 마이크를 +수신하고 사람이 말하기 시작하면 어시스턴트 재생을 지울 수 있습니다. 이렇게 하면 +어시스턴트 재생 중 공유 BlackHole local loopback 입력이 일시적으로 억제되더라도 +사람의 음성이 어시스턴트 출력보다 앞서 유지됩니다. `chrome.audioInputCommand` 및 +`chrome.audioOutputCommand`와 마찬가지로 이는 운영자가 구성하는 로컬 명령입니다. +명시적으로 신뢰할 수 있는 명령 경로나 인수 목록을 사용하고, 신뢰할 수 없는 위치의 +스크립트를 가리키지 마세요. + +`googlemeet speak`는 Chrome 세션의 활성 토크백 오디오 브리지를 트리거합니다. +`googlemeet leave`는 해당 브리지를 중지합니다. Voice Call Plugin을 통해 위임된 +Twilio 세션의 경우 `leave`는 기반 음성 통화도 끊습니다. API로 관리되는 공간의 +활성 Google Meet 회의도 닫으려면 `googlemeet end-active-conference`를 사용하세요. ## 관련 항목 -- [Voice Call Plugin](/ko/plugins/voice-call) -- [대화 모드](/ko/nodes/talk) -- [Plugin 빌드하기](/ko/plugins/building-plugins) +- [Voice call Plugin](/ko/plugins/voice-call) +- [토크 모드](/ko/nodes/talk) +- [Plugin 빌드](/ko/plugins/building-plugins) diff --git a/docs/ko/reference/RELEASING.md b/docs/ko/reference/RELEASING.md index 79b4f37cb..44e0f3c98 100644 --- a/docs/ko/reference/RELEASING.md +++ b/docs/ko/reference/RELEASING.md @@ -1,24 +1,24 @@ --- read_when: - 공개 릴리스 채널 정의를 찾는 중 - - 릴리스 검증 또는 패키지 승인 실행 - - 버전 명명 규칙과 주기를 찾는 중 -summary: 릴리스 레인, 운영자 체크리스트, 검증 박스, 버전 명명, 주기 + - 릴리스 검증 또는 패키지 인수 실행 + - 버전 명명 방식 및 릴리스 주기 확인 +summary: 릴리스 레인, 운영자 체크리스트, 검증 박스, 버전 명명 및 주기 title: 릴리스 정책 x-i18n: - generated_at: "2026-05-03T21:37:12Z" + generated_at: "2026-05-04T07:02:59Z" model: gpt-5.5 provider: openai - source_hash: 566088d826e1e2bac21b11443b82b62cb73ed1fd9c508c3fb865149cf8a428ba + source_hash: ef50d3ef5d1e23b4e2c2b097fc4ca9f6d46bf8acb9aea0c9bca6d14e213b88b6 source_path: reference/RELEASING.md workflow: 16 --- -OpenClaw에는 세 가지 공개 릴리스 라인이 있습니다. +OpenClaw에는 세 가지 공개 릴리스 레인이 있습니다. -- stable: 기본적으로 npm `beta`에 게시되거나 명시적으로 요청하면 npm `latest`에 게시되는 태그된 릴리스 -- beta: npm `beta`에 게시되는 프리릴리스 태그 -- dev: `main`의 이동하는 헤드 +- 안정: 기본적으로 npm `beta`에 게시하거나 명시적으로 요청된 경우 npm `latest`에 게시하는 태그 릴리스 +- 베타: npm `beta`에 게시하는 프리릴리스 태그 +- 개발: `main`의 이동하는 헤드 ## 버전 명명 @@ -28,141 +28,249 @@ OpenClaw에는 세 가지 공개 릴리스 라인이 있습니다. - Git 태그: `vYYYY.M.D-N` - 베타 프리릴리스 버전: `YYYY.M.D-beta.N` - Git 태그: `vYYYY.M.D-beta.N` -- 월이나 일을 0으로 채우지 마세요 +- 월 또는 일을 0으로 채우지 마세요 - `latest`는 현재 승격된 안정 npm 릴리스를 의미합니다 - `beta`는 현재 베타 설치 대상을 의미합니다 -- 안정 및 안정 수정 릴리스는 기본적으로 npm `beta`에 게시됩니다. 릴리스 운영자는 명시적으로 `latest`를 대상으로 지정하거나, 검증된 베타 빌드를 나중에 승격할 수 있습니다 +- 안정 및 안정 수정 릴리스는 기본적으로 npm `beta`에 게시됩니다. 릴리스 운영자는 `latest`를 명시적으로 대상으로 지정하거나, 검증된 베타 빌드를 나중에 승격할 수 있습니다 - 모든 안정 OpenClaw 릴리스는 npm 패키지와 macOS 앱을 함께 제공합니다. - 베타 릴리스는 일반적으로 먼저 npm/패키지 경로를 검증하고 게시하며, - mac 앱 빌드/서명/공증은 명시적으로 요청되지 않는 한 안정 릴리스용으로 남겨둡니다 + 베타 릴리스는 일반적으로 npm/패키지 경로를 먼저 검증하고 게시하며, + Mac 앱 빌드/서명/공증은 명시적으로 요청되지 않는 한 안정 릴리스용으로 남겨둡니다 ## 릴리스 주기 - 릴리스는 베타 우선으로 진행됩니다 - 안정 릴리스는 최신 베타가 검증된 후에만 이어집니다 -- 유지관리자는 일반적으로 현재 `main`에서 생성한 `release/YYYY.M.D` 브랜치에서 릴리스를 만들기 때문에, - 릴리스 검증과 수정이 `main`의 새로운 개발을 막지 않습니다 -- 베타 태그가 푸시되었거나 게시되었고 수정이 필요한 경우, 유지관리자는 이전 베타 태그를 삭제하거나 다시 만드는 대신 - 다음 `-beta.N` 태그를 만듭니다 -- 자세한 릴리스 절차, 승인, 자격 증명, 복구 참고 사항은 +- 유지관리자는 일반적으로 현재 `main`에서 만든 `release/YYYY.M.D` 브랜치에서 릴리스를 자르므로, + 릴리스 검증과 수정이 `main`의 새 개발을 막지 않습니다 +- 베타 태그가 푸시되었거나 게시된 뒤 수정이 필요한 경우, 유지관리자는 이전 베타 태그를 삭제하거나 다시 만들지 않고 + 다음 `-beta.N` 태그를 자릅니다 +- 자세한 릴리스 절차, 승인, 자격 증명, 복구 노트는 유지관리자 전용입니다 ## 릴리스 운영자 체크리스트 이 체크리스트는 릴리스 흐름의 공개 형태입니다. 비공개 자격 증명, -서명, 공증, dist-tag 복구, 긴급 롤백 세부 사항은 유지관리자 전용 -릴리스 런북에 남겨둡니다. +서명, 공증, dist-tag 복구, 긴급 롤백 세부 정보는 +유지관리자 전용 릴리스 런북에 남아 있습니다. -1. 현재 `main`에서 시작합니다. 최신 변경 사항을 가져오고, 대상 커밋이 푸시되었는지 확인하며, +1. 현재 `main`에서 시작합니다. 최신 내용을 pull하고, 대상 커밋이 푸시되었는지 확인하며, 현재 `main` CI가 브랜치를 만들기에 충분히 정상인지 확인합니다. -2. 실제 커밋 기록을 기반으로 `/changelog`를 사용해 최상단 `CHANGELOG.md` 섹션을 다시 작성하고, - 항목을 사용자 지향으로 유지한 뒤 커밋하고, 푸시하고, 브랜치를 만들기 전에 한 번 더 리베이스/풀합니다. +2. 실제 커밋 기록을 바탕으로 `/changelog`로 최상단 `CHANGELOG.md` 섹션을 다시 작성하고, + 항목은 사용자 대상 내용으로 유지한 뒤 커밋하고 푸시하며, 브랜치하기 전에 + 한 번 더 rebase/pull합니다. 3. `src/plugins/compat/registry.ts` 및 - `src/commands/doctor/shared/deprecation-compat.ts`의 릴리스 호환성 기록을 검토합니다. 업그레이드 경로가 계속 보장될 때만 만료된 + `src/commands/doctor/shared/deprecation-compat.ts`의 릴리스 호환성 기록을 검토합니다. 업그레이드 경로가 계속 보장되는 경우에만 만료된 호환성을 제거하거나, 의도적으로 유지하는 이유를 기록합니다. 4. 현재 `main`에서 `release/YYYY.M.D`를 만듭니다. 일반 릴리스 작업을 `main`에서 직접 수행하지 마세요. 5. 의도한 태그에 필요한 모든 버전 위치를 올리고, - 게시 가능한 Plugin 패키지가 릴리스 버전과 호환성 메타데이터를 공유하도록 `pnpm plugins:sync`를 실행한 다음, 로컬 결정적 프리플라이트를 실행합니다. + `pnpm plugins:sync`를 실행하여 게시 가능한 Plugin 패키지가 릴리스 + 버전과 호환성 메타데이터를 공유하게 한 다음, 로컬 결정적 사전 점검을 실행합니다: `pnpm check:test-types`, `pnpm check:architecture`, - `pnpm build && pnpm ui:build`, `pnpm plugins:sync:check`, 그리고 + `pnpm build && pnpm ui:build`, `pnpm plugins:sync:check`, 및 `pnpm release:check`. -6. `preflight_only=true`로 `OpenClaw NPM Release`를 실행합니다. 태그가 존재하기 전에는, - 검증 전용 프리플라이트에 전체 40자 릴리스 브랜치 SHA를 사용할 수 있습니다. - 성공한 `preflight_run_id`를 저장합니다. -7. 릴리스 브랜치, 태그, 또는 전체 커밋 SHA에 대해 `Full Release Validation`으로 모든 프리릴리스 테스트를 시작합니다. - 이는 네 가지 큰 릴리스 테스트 박스인 Vitest, Docker, QA Lab, Package를 위한 단일 수동 진입점입니다. -8. 검증이 실패하면 릴리스 브랜치에서 수정하고, 수정이 입증되는 가장 작은 실패 파일, 라인, 워크플로 작업, 패키지 프로필, provider, 또는 model 허용 목록을 다시 실행합니다. - 변경된 표면 때문에 이전 증거가 무효화될 때만 전체 엄브렐라를 다시 실행합니다. -9. 베타의 경우 `vYYYY.M.D-beta.N`을 태그한 다음, 일치하는 `release/YYYY.M.D` 브랜치에서 `OpenClaw Release Publish`를 실행합니다. - 이 작업은 `pnpm plugins:sync:check`를 확인하고, - 먼저 게시 가능한 모든 Plugin 패키지를 npm에 게시하며, 두 번째로 같은 세트를 ClawPack npm-pack tarball로 ClawHub에 게시한 다음, - 일치하는 dist-tag로 준비된 OpenClaw npm 프리플라이트 아티팩트를 승격합니다. 게시 후에는 게시된 `openclaw@YYYY.M.D-beta.N` 또는 +6. `preflight_only=true`로 `OpenClaw NPM Release`를 실행합니다. 태그가 존재하기 전에는 + 전체 40자 릴리스 브랜치 SHA를 검증 전용 + 사전 점검에 사용할 수 있습니다. 성공한 `preflight_run_id`를 저장합니다. +7. 릴리스 브랜치, 태그 또는 전체 커밋 SHA에 대해 `Full Release Validation`으로 + 모든 프리릴리스 테스트를 시작합니다. 이는 네 개의 큰 릴리스 테스트 박스인 + Vitest, Docker, QA Lab, Package를 위한 단일 수동 진입점입니다. +8. 검증이 실패하면 릴리스 브랜치에서 수정하고, 수정이 입증되는 가장 작은 실패 + 파일, 레인, 워크플로 작업, 패키지 프로필, 제공자 또는 모델 허용 목록을 다시 실행합니다. + 변경된 표면 때문에 이전 증거가 오래된 경우에만 전체 우산 검증을 다시 실행합니다. +9. 베타의 경우 `vYYYY.M.D-beta.N` 태그를 지정한 다음, 일치하는 + `release/YYYY.M.D` 브랜치에서 `OpenClaw Release Publish`를 실행합니다. 이는 `pnpm plugins:sync:check`를 검증하고, + 모든 게시 가능한 Plugin 패키지를 먼저 npm에 게시한 뒤, 같은 + 세트를 두 번째로 ClawPack npm-pack tarball로 ClawHub에 게시하고, 그다음 일치하는 dist-tag로 + 준비된 OpenClaw npm 사전 점검 아티팩트를 승격합니다. 게시 후에는 게시된 `openclaw@YYYY.M.D-beta.N` 또는 `openclaw@beta` 패키지에 대해 게시 후 패키지 - 수락 검사를 실행합니다. 푸시되었거나 게시된 프리릴리스에 수정이 필요하면, - 다음으로 일치하는 프리릴리스 번호를 만드세요. 이전 프리릴리스를 삭제하거나 다시 쓰지 마세요. -10. 안정 릴리스의 경우, 검증된 베타 또는 릴리스 후보가 필요한 검증 증거를 갖춘 후에만 계속합니다. - 안정 npm 게시도 `OpenClaw Release Publish`를 통해 진행되며, - `preflight_run_id`로 성공한 프리플라이트 아티팩트를 재사용합니다. 안정 macOS 릴리스 준비 상태에는 - 패키징된 `.zip`, `.dmg`, `.dSYM.zip`과 `main`의 업데이트된 `appcast.xml`도 필요합니다. -11. 게시 후에는 npm 게시 후 검증기, 게시 후 채널 증거가 필요할 때 선택적으로 독립 실행형 - 게시된-npm Telegram E2E, 필요 시 dist-tag 승격, 일치하는 전체 `CHANGELOG.md` 섹션의 GitHub 릴리스/프리릴리스 노트, - 그리고 릴리스 공지 단계를 실행합니다. + 수락 검사를 실행합니다. 푸시되었거나 게시된 프리릴리스에 수정이 필요하면 + 다음으로 일치하는 프리릴리스 번호를 자르세요. 이전 + 프리릴리스를 삭제하거나 다시 쓰지 마세요. +10. 안정 릴리스의 경우, 검증된 베타 또는 릴리스 후보에 필요한 + 검증 증거가 있을 때만 계속합니다. 안정 npm 게시 역시 + `OpenClaw Release Publish`를 통해 진행하며, + `preflight_run_id`를 통해 성공한 사전 점검 아티팩트를 재사용합니다. 안정 macOS 릴리스 준비 상태에는 + 패키징된 `.zip`, `.dmg`, `.dSYM.zip` 및 `main`의 업데이트된 `appcast.xml`도 필요합니다. +11. 게시 후에는 npm 게시 후 검증기를 실행하고, 게시 후 채널 증거가 필요할 때 선택적 독립 실행형 + 게시된 npm Telegram E2E를 실행하며, + 필요 시 dist-tag 승격, 일치하는 전체 `CHANGELOG.md` 섹션의 GitHub 릴리스/프리릴리스 노트, + 릴리스 공지 단계를 수행합니다. -## 릴리스 프리플라이트 +## 릴리스 사전 점검 -- 릴리스 사전 점검 전에 `pnpm check:test-types`를 실행하여 더 빠른 로컬 `pnpm check` 게이트 밖에서도 테스트 TypeScript가 계속 적용되도록 합니다 -- 릴리스 사전 점검 전에 `pnpm check:architecture`를 실행하여 더 넓은 import cycle 및 아키텍처 경계 검사가 더 빠른 로컬 게이트 밖에서도 통과하도록 합니다 -- `pnpm release:check` 전에 `pnpm build && pnpm ui:build`를 실행하여 pack 검증 단계에 필요한 `dist/*` 릴리스 아티팩트와 Control UI 번들이 존재하도록 합니다 -- 루트 버전 범프 후, 태그 지정 전에 `pnpm plugins:sync`를 실행합니다. 이 명령은 게시 가능한 Plugin 패키지 버전, OpenClaw 피어/API 호환성 메타데이터, 빌드 메타데이터, Plugin 변경 로그 스텁을 코어 릴리스 버전에 맞게 업데이트합니다. `pnpm plugins:sync:check`는 변경하지 않는 릴리스 가드입니다. 이 단계를 잊은 경우, 게시 워크플로는 레지스트리 변경 전에 실패합니다. -- 릴리스 승인 전에 수동 `Full Release Validation` 워크플로를 실행하여 모든 사전 릴리스 테스트 박스를 하나의 진입점에서 시작합니다. 이 워크플로는 브랜치, 태그 또는 전체 커밋 SHA를 받고, 수동 `CI`를 디스패치하며, 설치 스모크, 패키지 승인, Docker 릴리스 경로 스위트, 라이브/E2E, OpenWebUI, QA Lab 패리티, Matrix, Telegram 레인을 위한 `OpenClaw Release Checks`를 디스패치합니다. `release_profile=full` 및 `rerun_group=all`을 사용하면, 릴리스 검사에서 생성된 `release-package-under-test` 아티팩트를 대상으로 패키지 Telegram E2E도 실행합니다. 게시 후 같은 Telegram E2E가 게시된 npm 패키지도 검증해야 하는 경우 `npm_telegram_package_spec`을 제공합니다. 게시 후 Package Acceptance가 SHA로 빌드된 아티팩트 대신 배포된 npm 패키지를 대상으로 패키지/업데이트 매트릭스를 실행해야 하는 경우 `package_acceptance_package_spec`을 제공합니다. Telegram E2E를 강제하지 않고 비공개 증거 보고서가 검증 대상이 게시된 npm 패키지와 일치함을 증명해야 하는 경우 `evidence_package_spec`을 제공합니다. +- 더 빠른 로컬 `pnpm check` 게이트 밖에서도 테스트 TypeScript가 + 계속 포함되도록 릴리스 사전 검증 전에 `pnpm check:test-types`를 실행합니다 +- 더 빠른 로컬 게이트 밖에서도 더 넓은 import + cycle 및 아키텍처 경계 검사가 통과되도록 릴리스 사전 검증 전에 `pnpm check:architecture`를 실행합니다 +- pack + 검증 단계에 필요한 예상 `dist/*` 릴리스 아티팩트와 Control UI 번들이 존재하도록 + `pnpm release:check` 전에 `pnpm build && pnpm ui:build`를 실행합니다 +- 루트 버전 올림 이후, 태그 지정 전에 `pnpm plugins:sync`를 실행합니다. 이 명령은 + 게시 가능한 Plugin 패키지 버전, OpenClaw 피어/API 호환성 + 메타데이터, 빌드 메타데이터, Plugin 변경 로그 스텁을 코어 + 릴리스 버전에 맞게 업데이트합니다. `pnpm plugins:sync:check`는 변경을 만들지 않는 릴리스 가드입니다. + 이 단계를 잊으면 게시 워크플로는 레지스트리 변경 전에 실패합니다. +- 릴리스 승인 전에 수동 `Full Release Validation` 워크플로를 실행해 + 모든 사전 릴리스 테스트 박스를 하나의 진입점에서 시작합니다. 이 워크플로는 브랜치, + 태그 또는 전체 커밋 SHA를 받고, 수동 `CI`를 디스패치하며, + 설치 스모크, 패키지 승인, Docker + 릴리스 경로 스위트, 라이브/E2E, OpenWebUI, QA Lab 패리티, Matrix, Telegram + 레인을 위한 `OpenClaw Release Checks`를 디스패치합니다. `release_profile=full` 및 `rerun_group=all`을 사용하면 릴리스 + 검사에서 나온 `release-package-under-test` 아티팩트에 대해 패키지 + Telegram E2E도 실행합니다. 동일한 + Telegram E2E가 게시된 npm 패키지도 검증해야 하는 경우 게시 후 + `npm_telegram_package_spec`을 제공합니다. Package Acceptance가 SHA로 빌드한 아티팩트가 아니라 + 출시된 npm 패키지에 대해 패키지/업데이트 매트릭스를 실행해야 하는 경우 게시 후 + `package_acceptance_package_spec`을 제공합니다. + Telegram E2E를 강제하지 않고도 비공개 증거 보고서가 검증이 게시된 npm 패키지와 일치함을 증명해야 할 때는 + `evidence_package_spec`을 제공합니다. 예: `gh workflow run full-release-validation.yml --ref main -f ref=release/YYYY.M.D` -- 릴리스 작업이 계속되는 동안 패키지 후보에 대한 사이드 채널 증거가 필요할 때 수동 `Package Acceptance` 워크플로를 실행합니다. `openclaw@beta`, `openclaw@latest` 또는 정확한 릴리스 버전에는 `source=npm`을 사용하고, 현재 `workflow_ref` 하네스로 신뢰할 수 있는 `package_ref` 브랜치/태그/SHA를 pack하려면 `source=ref`를 사용하며, 필수 SHA-256이 있는 HTTPS tarball에는 `source=url`을 사용하거나, 다른 GitHub Actions 실행에서 업로드한 tarball에는 `source=artifact`를 사용합니다. 이 워크플로는 후보를 `package-under-test`로 해석하고, 해당 tarball을 대상으로 Docker E2E 릴리스 스케줄러를 재사용하며, `telegram_mode=mock-openai` 또는 `telegram_mode=live-frontier`로 같은 tarball에 대해 Telegram QA를 실행할 수 있습니다. 선택된 Docker 레인에 `published-upgrade-survivor`가 포함된 경우, 패키지 아티팩트가 후보가 되고 `published_upgrade_survivor_baseline`이 게시된 기준 버전을 선택합니다. +- 릴리스 작업이 계속되는 동안 패키지 후보에 대한 보조 채널 증거가 필요하면 + 수동 `Package Acceptance` 워크플로를 실행합니다. `openclaw@beta`, + `openclaw@latest` 또는 정확한 릴리스 버전에는 `source=npm`을 사용합니다. 현재 + `workflow_ref` 하네스로 신뢰된 `package_ref` 브랜치/태그/SHA를 패키징하려면 `source=ref`를 사용합니다. + 필수 SHA-256이 있는 HTTPS tarball에는 `source=url`을 사용합니다. + 다른 GitHub + Actions 실행에서 업로드한 tarball에는 `source=artifact`를 사용합니다. 워크플로는 후보를 + `package-under-test`로 해석하고, 해당 + tarball에 대해 Docker E2E 릴리스 스케줄러를 재사용하며, + `telegram_mode=mock-openai` 또는 `telegram_mode=live-frontier`로 동일한 tarball에 대해 Telegram QA를 실행할 수 있습니다. 선택한 Docker 레인에 + `published-upgrade-survivor`가 포함되면 패키지 + 아티팩트가 후보가 되고 `published_upgrade_survivor_baseline`이 + 게시된 기준선을 선택합니다. 예: `gh workflow run package-acceptance.yml --ref main -f workflow_ref=main -f source=npm -f package_spec=openclaw@beta -f suite_profile=product -f published_upgrade_survivor_baseline=openclaw@2026.4.26 -f telegram_mode=mock-openai` 일반 프로필: - - `smoke`: 설치/채널/에이전트, Gateway 네트워크 및 구성 다시 로드 레인 + - `smoke`: 설치/채널/에이전트, Gateway 네트워크, 구성 다시 로드 레인 - `package`: OpenWebUI 또는 라이브 ClawHub 없는 아티팩트 네이티브 패키지/업데이트/Plugin 레인 - - `product`: 패키지 프로필에 MCP 채널, cron/subagent 정리, OpenAI 웹 검색 및 OpenWebUI 추가 + - `product`: 패키지 프로필에 MCP 채널, Cron/하위 에이전트 정리, + OpenAI 웹 검색, OpenWebUI 추가 - `full`: OpenWebUI가 포함된 Docker 릴리스 경로 청크 - `custom`: 집중 재실행을 위한 정확한 `docker_lanes` 선택 -- 릴리스 후보에 대한 전체 일반 CI 적용 범위만 필요할 때는 수동 `CI` 워크플로를 직접 실행합니다. 수동 CI 디스패치는 변경 범위 지정을 우회하고 Linux Node 샤드, 번들 Plugin 샤드, 채널 계약, Node 22 호환성, `check`, `check-additional`, 빌드 스모크, 문서 검사, Python Skills, Windows, macOS, Android, Control UI i18n 레인을 강제로 실행합니다. +- 릴리스 후보에 대한 전체 일반 CI + 커버리지만 필요할 때는 수동 `CI` 워크플로를 직접 실행합니다. 수동 CI 디스패치는 변경 범위 지정을 우회하고 + Linux Node 샤드, 번들 Plugin 샤드, 채널 + 계약, Node 22 호환성, `check`, `check-additional`, 빌드 스모크, + 문서 검사, Python Skills, Windows, macOS, Android, Control UI i18n + 레인을 강제로 실행합니다. 예: `gh workflow run ci.yml --ref release/YYYY.M.D` -- 릴리스 텔레메트리를 검증할 때 `pnpm qa:otel:smoke`를 실행합니다. 이 명령은 로컬 OTLP/HTTP 수신기를 통해 QA-lab을 실행하고, Opik, Langfuse 또는 다른 외부 수집기 없이 내보낸 trace span 이름, 제한된 속성, 콘텐츠/식별자 수정 처리를 검증합니다. +- 릴리스 텔레메트리를 검증할 때 `pnpm qa:otel:smoke`를 실행합니다. 이 명령은 + 로컬 OTLP/HTTP 수신기를 통해 QA-lab을 실행하고, Opik, Langfuse 또는 다른 외부 수집기 없이 + 내보낸 trace + span 이름, 제한된 속성, 콘텐츠/식별자 수정 처리를 검증합니다. - 모든 태그 릴리스 전에 `pnpm release:check`를 실행합니다 -- 태그가 존재한 후 변경을 수행하는 게시 순서에는 `OpenClaw Release Publish`를 실행합니다. `release/YYYY.M.D`에서 디스패치하고(`main`에서 도달 가능한 태그를 게시할 때는 `main`), 릴리스 태그와 성공한 OpenClaw npm `preflight_run_id`를 전달하며, 의도적으로 집중 복구를 실행하는 경우가 아니라면 기본 Plugin 게시 범위 `all-publishable`을 유지합니다. 이 워크플로는 Plugin npm 게시, Plugin ClawHub 게시, OpenClaw npm 게시를 직렬화하여 외부화된 Plugin보다 코어 패키지가 먼저 게시되지 않도록 합니다. +- 태그가 존재한 후 변경을 수행하는 게시 시퀀스에는 `OpenClaw Release Publish`를 실행합니다. + `release/YYYY.M.D`에서 디스패치하고(`main`에 도달 가능한 태그를 게시할 때는 `main`), + 릴리스 태그와 성공한 OpenClaw npm + `preflight_run_id`를 전달하며, 의도적으로 집중 복구를 실행하는 경우가 아니면 기본 Plugin 게시 범위 + `all-publishable`을 유지합니다. 이 + 워크플로는 Plugin npm 게시, Plugin ClawHub 게시, OpenClaw + npm 게시를 직렬화하여 외부화된 + Plugin보다 코어 패키지가 먼저 게시되지 않도록 합니다. - 릴리스 검사는 이제 별도의 수동 워크플로에서 실행됩니다: `OpenClaw Release Checks` -- `OpenClaw Release Checks`는 릴리스 승인 전에 QA Lab mock 패리티 레인과 빠른 라이브 Matrix 프로필 및 Telegram QA 레인도 실행합니다. 라이브 레인은 `qa-live-shared` 환경을 사용하며, Telegram은 Convex CI 자격 증명 임대도 사용합니다. 전체 Matrix 전송, 미디어, E2EE 인벤토리를 병렬로 실행하려면 `matrix_profile=all` 및 `matrix_shards=true`로 수동 `QA-Lab - All Lanes` 워크플로를 실행합니다. -- Cross-OS 설치 및 업그레이드 런타임 검증은 공개 `OpenClaw Release Checks` 및 `Full Release Validation`의 일부이며, 이들은 재사용 가능 워크플로 `.github/workflows/openclaw-cross-os-release-checks-reusable.yml`을 직접 호출합니다 -- 이 분리는 의도된 것입니다. 실제 npm 릴리스 경로는 짧고 결정적이며 아티팩트 중심으로 유지하고, 더 느린 라이브 검사는 자체 레인에 두어 게시를 지연하거나 차단하지 않도록 합니다 -- 비밀을 포함하는 릴리스 검사는 `Full Release Validation`을 통해 또는 `main`/릴리스 워크플로 ref에서 디스패치하여 워크플로 로직과 비밀이 통제되도록 해야 합니다 -- `OpenClaw Release Checks`는 해석된 커밋이 OpenClaw 브랜치 또는 릴리스 태그에서 도달 가능한 한 브랜치, 태그 또는 전체 커밋 SHA를 받습니다 -- `OpenClaw NPM Release` 검증 전용 사전 점검은 푸시된 태그 없이도 현재 전체 40자 워크플로 브랜치 커밋 SHA를 받습니다 +- `OpenClaw Release Checks`는 릴리스 승인 전에 QA Lab mock 패리티 레인과 빠른 + 라이브 Matrix 프로필 및 Telegram QA 레인도 실행합니다. 라이브 + 레인은 `qa-live-shared` 환경을 사용하고, Telegram은 Convex CI + 자격 증명 임대도 사용합니다. 전체 Matrix + 전송, 미디어, E2EE 인벤토리를 병렬로 확인하려면 + `matrix_profile=all` 및 `matrix_shards=true`로 수동 `QA-Lab - All Lanes` 워크플로를 실행합니다. +- Cross-OS 설치 및 업그레이드 런타임 검증은 공개 + `OpenClaw Release Checks` 및 `Full Release Validation`의 일부이며, 이들은 재사용 가능한 워크플로 + `.github/workflows/openclaw-cross-os-release-checks-reusable.yml`을 직접 호출합니다 +- 이 분리는 의도적입니다. 실제 npm 릴리스 경로는 짧고, + 결정적이며 아티팩트 중심으로 유지하고, 더 느린 라이브 검사는 게시를 지연하거나 차단하지 않도록 + 자체 레인에 둡니다 +- 비밀을 포함하는 릴리스 검사는 `Full Release +Validation`을 통해 또는 `main`/릴리스 워크플로 ref에서 디스패치하여 워크플로 로직과 + 비밀이 통제되도록 해야 합니다 +- `OpenClaw Release Checks`는 해석된 커밋이 OpenClaw 브랜치 또는 릴리스 태그에서 도달 가능하다면 + 브랜치, 태그 또는 전체 커밋 SHA를 받습니다 +- `OpenClaw NPM Release` 검증 전용 사전 검증도 푸시된 태그 없이 현재 + 전체 40자 워크플로 브랜치 커밋 SHA를 받습니다 - 해당 SHA 경로는 검증 전용이며 실제 게시로 승격할 수 없습니다 -- SHA 모드에서 워크플로는 패키지 메타데이터 검사에만 `v`을 합성합니다. 실제 게시에는 여전히 실제 릴리스 태그가 필요합니다 -- 두 워크플로 모두 실제 게시 및 승격 경로는 GitHub 호스팅 러너에서 유지하고, 변경하지 않는 검증 경로는 더 큰 Blacksmith Linux 러너를 사용할 수 있습니다 -- 해당 워크플로는 `OPENAI_API_KEY` 및 `ANTHROPIC_API_KEY` 워크플로 비밀을 모두 사용하여 - `OPENCLAW_LIVE_TEST=1 OPENCLAW_LIVE_CACHE_TEST=1 pnpm test:live:cache` - 를 실행합니다 -- npm 릴리스 사전 점검은 더 이상 별도의 릴리스 검사 레인을 기다리지 않습니다 -- 승인 전에 `RELEASE_TAG=vYYYY.M.D node --import tsx scripts/openclaw-npm-release-check.ts` - (또는 일치하는 beta/correction 태그)를 실행합니다 -- npm 게시 후, 새로운 임시 prefix에서 게시된 레지스트리 설치 경로를 검증하려면 +- SHA 모드에서 워크플로는 패키지 메타데이터 검사에만 `v`을 합성합니다. + 실제 게시에는 여전히 실제 릴리스 태그가 필요합니다 +- 두 워크플로 모두 실제 게시 및 승격 경로는 GitHub 호스팅 + 러너에서 유지하고, 변경하지 않는 검증 경로는 더 큰 + Blacksmith Linux 러너를 사용할 수 있습니다 +- 해당 워크플로는 + `OPENCLAW_LIVE_TEST=1 OPENCLAW_LIVE_CACHE_TEST=1 pnpm test:live:cache`를 + `OPENAI_API_KEY` 및 `ANTHROPIC_API_KEY` 워크플로 비밀과 함께 실행합니다 +- npm 릴리스 사전 검증은 더 이상 별도 릴리스 검사 레인을 기다리지 않습니다 +- 승인 전에 + `RELEASE_TAG=vYYYY.M.D node --import tsx scripts/openclaw-npm-release-check.ts` + (또는 일치하는 베타/수정 태그)를 실행합니다 +- npm 게시 후, `node --import tsx scripts/openclaw-npm-postpublish-verify.ts YYYY.M.D` - (또는 일치하는 beta/correction 버전)를 실행합니다 -- beta 게시 후, 공유 임대 Telegram 자격 증명 풀을 사용하여 게시된 npm 패키지에 대해 설치된 패키지 온보딩, Telegram 설정 및 실제 Telegram E2E를 검증하려면 `OPENCLAW_NPM_TELEGRAM_PACKAGE_SPEC=openclaw@YYYY.M.D-beta.N OPENCLAW_NPM_TELEGRAM_CREDENTIAL_SOURCE=convex OPENCLAW_NPM_TELEGRAM_CREDENTIAL_ROLE=ci pnpm test:docker:npm-telegram-live`를 실행합니다. 로컬 메인테이너의 일회성 실행은 Convex 변수를 생략하고 세 개의 `OPENCLAW_QA_TELEGRAM_*` env 자격 증명을 직접 전달할 수 있습니다. -- 메인테이너는 수동 `NPM Telegram Beta E2E` 워크플로를 통해 GitHub Actions에서 같은 게시 후 검사를 실행할 수 있습니다. 이는 의도적으로 수동 전용이며 모든 merge마다 실행되지 않습니다. -- 메인테이너 릴리스 자동화는 이제 사전 점검 후 승격 방식을 사용합니다: + (또는 일치하는 베타/수정 버전)를 실행해 새 임시 prefix에서 게시된 레지스트리 + 설치 경로를 검증합니다 +- 베타 게시 후, 공유 임대 Telegram 자격 증명 + 풀을 사용해 게시된 npm 패키지에 대해 설치 패키지 온보딩, Telegram 설정, 실제 Telegram E2E를 검증하려면 + `OPENCLAW_NPM_TELEGRAM_PACKAGE_SPEC=openclaw@YYYY.M.D-beta.N OPENCLAW_NPM_TELEGRAM_CREDENTIAL_SOURCE=convex OPENCLAW_NPM_TELEGRAM_CREDENTIAL_ROLE=ci pnpm test:docker:npm-telegram-live`를 실행합니다. + 로컬 유지관리자 일회성 실행은 Convex 변수를 생략하고 세 개의 + `OPENCLAW_QA_TELEGRAM_*` env 자격 증명을 직접 전달할 수 있습니다. +- 유지관리자 머신에서 전체 게시 후 베타 스모크를 실행하려면 `pnpm release:beta-smoke -- --beta betaN`을 사용합니다. 이 헬퍼는 Parallels npm 업데이트/새 대상 검증을 실행하고, `NPM Telegram Beta E2E`를 디스패치하며, 정확한 워크플로 실행을 폴링하고, 아티팩트를 다운로드한 뒤 Telegram 보고서를 출력합니다. +- 유지관리자는 GitHub Actions에서 수동 + `NPM Telegram Beta E2E` 워크플로를 통해 동일한 게시 후 검사를 실행할 수 있습니다. 이는 의도적으로 수동 전용이며 + 모든 병합마다 실행되지 않습니다. +- 유지관리자 릴리스 자동화는 이제 사전 검증 후 승격을 사용합니다: - 실제 npm 게시에는 성공한 npm `preflight_run_id`가 필요합니다 - - 실제 npm 게시는 성공한 사전 점검 실행과 같은 `main` 또는 `release/YYYY.M.D` 브랜치에서 디스패치되어야 합니다 - - 안정 npm 릴리스는 기본적으로 `beta`를 사용합니다 + - 실제 npm 게시는 성공한 사전 검증 실행과 동일한 `main` 또는 + `release/YYYY.M.D` 브랜치에서 디스패치되어야 합니다 + - 안정 npm 릴리스의 기본값은 `beta`입니다 - 안정 npm 게시는 워크플로 입력을 통해 명시적으로 `latest`를 대상으로 할 수 있습니다 - - 토큰 기반 npm dist-tag 변경은 이제 보안을 위해 `openclaw/releases-private/.github/workflows/openclaw-npm-dist-tags.yml`에 있습니다. `npm dist-tag add`에는 여전히 `NPM_TOKEN`이 필요하지만 공개 저장소는 OIDC 전용 게시를 유지하기 때문입니다 - - 공개 `macOS Release`는 검증 전용입니다. 태그가 릴리스 브랜치에만 있고 워크플로가 `main`에서 디스패치되는 경우 `public_release_branch=release/YYYY.M.D`를 설정합니다 - - 실제 비공개 mac 게시에는 성공한 비공개 mac `preflight_run_id` 및 `validate_run_id`가 필요합니다 - - 실제 게시 경로는 다시 빌드하지 않고 준비된 아티팩트를 승격합니다 -- `YYYY.M.D-N` 같은 안정 correction 릴리스의 경우, 게시 후 검증기는 `YYYY.M.D`에서 `YYYY.M.D-N`으로의 같은 임시 prefix 업그레이드 경로도 확인하여 릴리스 correction이 이전 전역 설치를 기본 안정 페이로드에 조용히 남겨두지 못하게 합니다 -- npm 릴리스 사전 점검은 tarball에 `dist/control-ui/index.html`과 비어 있지 않은 `dist/control-ui/assets/` 페이로드가 모두 포함되어 있지 않으면 닫힌 상태로 실패하므로, 빈 브라우저 대시보드를 다시 배포하지 않습니다 -- 게시 후 검증은 게시된 Plugin entrypoint와 패키지 메타데이터가 설치된 레지스트리 레이아웃에 존재하는지도 확인합니다. 누락된 Plugin 런타임 페이로드를 배포하는 릴리스는 postpublish 검증기에 실패하며 `latest`로 승격될 수 없습니다. -- `pnpm test:install:smoke`는 후보 업데이트 tarball에 npm pack `unpackedSize` 예산도 적용하므로, 설치 프로그램 e2e가 릴리스 게시 경로 전에 의도치 않은 pack 비대를 포착합니다 -- 릴리스 작업이 CI 계획, 확장 타이밍 매니페스트 또는 확장 테스트 매트릭스를 건드린 경우, 승인 전에 `.github/workflows/plugin-prerelease.yml`의 planner 소유 `plugin-prerelease-extension-shard` 매트릭스 출력을 다시 생성하고 검토하여 릴리스 노트가 오래된 CI 레이아웃을 설명하지 않도록 합니다 -- 안정 macOS 릴리스 준비에는 updater 표면도 포함됩니다: - - GitHub 릴리스에는 패키징된 `.zip`, `.dmg`, `.dSYM.zip`이 최종적으로 포함되어야 합니다 - - 게시 후 `main`의 `appcast.xml`은 새 안정 zip을 가리켜야 합니다 - - 패키징된 앱은 non-debug bundle id, 비어 있지 않은 Sparkle feed URL, 그리고 해당 릴리스 버전의 표준 Sparkle build floor 이상인 `CFBundleVersion`을 유지해야 합니다 + - 토큰 기반 npm dist-tag 변경은 이제 보안을 위해 + `openclaw/releases-private/.github/workflows/openclaw-npm-dist-tags.yml`에 있습니다. 이는 + `npm dist-tag add`에는 여전히 `NPM_TOKEN`이 필요하고, 공개 + 저장소는 OIDC 전용 게시를 유지하기 때문입니다 + - 공개 `macOS Release`는 검증 전용입니다. 태그가 릴리스 브랜치에만 있고 + 워크플로가 `main`에서 디스패치되는 경우 + `public_release_branch=release/YYYY.M.D`를 설정합니다 + - 실제 비공개 mac 게시에는 성공한 비공개 mac + `preflight_run_id` 및 `validate_run_id`가 필요합니다 + - 실제 게시 경로는 아티팩트를 다시 빌드하는 대신 준비된 아티팩트를 승격합니다 +- `YYYY.M.D-N` 같은 안정 수정 릴리스의 경우, 게시 후 검증기는 + 동일한 임시 prefix 업그레이드 경로도 `YYYY.M.D`에서 `YYYY.M.D-N`으로 확인하여 + 릴리스 수정이 오래된 전역 설치를 기본 안정 payload에 조용히 남겨두지 못하게 합니다 +- npm 릴리스 사전 검증은 tarball에 + `dist/control-ui/index.html`과 비어 있지 않은 `dist/control-ui/assets/` payload가 모두 포함되지 않으면 닫힌 상태로 실패하여 + 빈 브라우저 대시보드를 다시 출시하지 않도록 합니다 +- 게시 후 검증은 게시된 Plugin 진입점과 + 패키지 메타데이터가 설치된 레지스트리 레이아웃에 존재하는지도 확인합니다. 누락된 Plugin 런타임 payload를 포함해 출시된 릴리스는 postpublish 검증기에 실패하며 + `latest`로 승격될 수 없습니다. +- `pnpm test:install:smoke`는 후보 업데이트 tarball에 대해 npm pack `unpackedSize` 예산도 강제하므로 + 설치 프로그램 e2e가 릴리스 게시 경로 전에 의도치 않은 pack 비대를 잡아냅니다 +- 릴리스 작업이 CI 계획, extension 타이밍 manifest 또는 + extension 테스트 매트릭스를 건드렸다면, 승인 전에 + `.github/workflows/plugin-prerelease.yml`의 플래너 소유 + `plugin-prerelease-extension-shard` 매트릭스 출력을 다시 생성하고 검토하여 릴리스 노트가 + 오래된 CI 레이아웃을 설명하지 않도록 합니다 +- 안정 macOS 릴리스 준비 상태에는 업데이트 표면도 포함됩니다: + - GitHub 릴리스에는 패키징된 `.zip`, `.dmg`, `.dSYM.zip`이 최종 포함되어야 합니다 + - `main`의 `appcast.xml`은 게시 후 새 안정 zip을 가리켜야 합니다 + - 패키징된 앱은 비디버그 번들 ID, 비어 있지 않은 Sparkle feed + URL, 그리고 해당 릴리스 버전의 표준 Sparkle 빌드 하한 이상인 `CFBundleVersion`을 유지해야 합니다 ## 릴리스 테스트 박스 -`Full Release Validation`은 운영자가 하나의 진입점에서 모든 사전 릴리스 테스트를 시작하는 방법입니다. 빠르게 움직이는 브랜치에서 고정된 커밋 증거가 필요하면, 모든 하위 워크플로가 대상 SHA에 고정된 임시 브랜치에서 실행되도록 helper를 사용합니다: +`Full Release Validation`은 운영자가 모든 사전 릴리스 테스트를 +하나의 진입점에서 시작하는 방법입니다. 빠르게 움직이는 브랜치에서 고정된 커밋 증거가 필요하면, +모든 하위 워크플로가 대상 +SHA에 고정된 임시 브랜치에서 실행되도록 헬퍼를 사용합니다: ```bash pnpm ci:full-release --sha ``` -helper는 `release-ci/-...`를 푸시하고, 해당 브랜치에서 `ref=`로 `Full Release Validation`을 디스패치하며, 모든 하위 워크플로의 `headSha`가 대상과 일치하는지 검증한 다음 임시 브랜치를 삭제합니다. 이렇게 하면 실수로 더 새로운 `main` 하위 실행을 증명하는 일을 피할 수 있습니다. +헬퍼는 `release-ci/-...`를 푸시하고, 해당 브랜치에서 `ref=`로 `Full Release Validation`을 +디스패치하며, 모든 하위 워크플로 `headSha`가 +대상과 일치하는지 확인한 다음 임시 브랜치를 삭제합니다. 이를 통해 실수로 더 최신 +`main` 하위 실행을 증명하는 일을 피할 수 있습니다. -릴리스 브랜치 또는 태그 검증의 경우, 신뢰할 수 있는 `main` 워크플로 ref에서 실행하고 릴리스 브랜치 또는 태그를 `ref`로 전달합니다: +릴리스 브랜치 또는 태그 검증의 경우, 신뢰된 `main` 워크플로 +ref에서 실행하고 릴리스 브랜치 또는 태그를 `ref`로 전달합니다: ```bash gh workflow run full-release-validation.yml \ @@ -174,19 +282,19 @@ gh workflow run full-release-validation.yml \ -f evidence_package_spec=openclaw@YYYY.M.D-beta.N ``` -워크플로는 대상 ref를 확인하고, `target_ref=`로 수동 `CI`를 디스패치하며, `OpenClaw Release Checks`를 디스패치하고, 패키지 대상 검사에 사용할 부모 `release-package-under-test` 아티팩트를 준비하며, `release_profile=full`이고 `rerun_group=all`이거나 `npm_telegram_package_spec`이 설정된 경우 독립 실행형 패키지 Telegram E2E를 디스패치합니다. 그런 다음 `OpenClaw Release Checks`는 설치 스모크, 교차 OS 릴리스 검사, 라이브/E2E Docker 릴리스 경로 커버리지, Telegram 패키지 QA가 포함된 패키지 승인, QA Lab 패리티, 라이브 Matrix, 라이브 Telegram으로 확장 실행됩니다. 전체 실행은 `Full Release Validation` 요약에서 `normal_ci`와 `release_checks`가 성공으로 표시될 때만 허용됩니다. full/all 모드에서는 `npm_telegram` 자식도 성공해야 합니다. full/all이 아닌 경우 게시된 `npm_telegram_package_spec`이 제공되지 않는 한 건너뜁니다. 최종 검증기 요약에는 각 자식 실행의 가장 느린 작업 테이블이 포함되어 릴리스 관리자가 로그를 다운로드하지 않고도 현재 핵심 경로를 확인할 수 있습니다. -전체 단계 매트릭스, 정확한 워크플로 작업 이름, stable 프로필과 full 프로필의 차이, 아티팩트, 집중 재실행 핸들은 [전체 릴리스 검증](/ko/reference/full-release-validation)을 참고하세요. -자식 워크플로는 `Full Release Validation`을 실행하는 신뢰할 수 있는 ref, 일반적으로 `--ref main`에서 디스패치됩니다. 대상 `ref`가 더 오래된 릴리스 브랜치나 태그를 가리키는 경우에도 마찬가지입니다. 별도의 Full Release Validation workflow-ref 입력은 없습니다. 워크플로 실행 ref를 선택하여 신뢰할 수 있는 하네스를 선택하세요. -이동하는 `main`에서 정확한 커밋 증명을 위해 `--ref main -f ref=`를 사용하지 마세요. 원시 커밋 SHA는 워크플로 디스패치 ref가 될 수 없으므로 `pnpm ci:full-release --sha `를 사용해 고정된 임시 브랜치를 생성하세요. +워크플로는 대상 ref를 해석하고, `target_ref=`로 수동 `CI`를 디스패치하며, `OpenClaw Release Checks`를 디스패치하고, 패키지 대상 검사용 상위 `release-package-under-test` 아티팩트를 준비하며, `release_profile=full`이고 `rerun_group=all`이거나 `npm_telegram_package_spec`이 설정된 경우 독립 실행형 패키지 Telegram E2E를 디스패치합니다. 그런 다음 `OpenClaw Release Checks`는 설치 스모크, 교차 OS 릴리스 검사, 라이브/E2E Docker 릴리스 경로 커버리지, Telegram 패키지 QA가 포함된 Package Acceptance, QA Lab 패리티, 라이브 Matrix, 라이브 Telegram으로 확장됩니다. 전체 실행은 `Full Release Validation` 요약에서 `normal_ci`와 `release_checks`가 성공으로 표시될 때만 허용됩니다. full/all 모드에서는 `npm_telegram` 하위 항목도 성공해야 합니다. full/all 외부에서는 게시된 `npm_telegram_package_spec`이 제공되지 않는 한 건너뜁니다. 최종 검증자 요약에는 각 하위 실행의 가장 느린 작업 표가 포함되어 있어, 릴리스 관리자가 로그를 다운로드하지 않고도 현재 주요 경로를 확인할 수 있습니다. +전체 단계 매트릭스, 정확한 워크플로 작업 이름, stable 프로필과 full 프로필의 차이, 아티팩트, 집중 재실행 핸들은 [전체 릴리스 검증](/ko/reference/full-release-validation)을 참조하세요. +하위 워크플로는 `Full Release Validation`을 실행하는 신뢰된 ref에서 디스패치되며, 대상 `ref`가 이전 릴리스 브랜치나 태그를 가리키더라도 일반적으로 `--ref main`입니다. 별도의 Full Release Validation workflow-ref 입력은 없습니다. 워크플로 실행 ref를 선택하여 신뢰된 하네스를 선택하세요. +이동 중인 `main`에서 정확한 커밋 증명에 `--ref main -f ref=`를 사용하지 마세요. 원시 커밋 SHA는 워크플로 디스패치 ref가 될 수 없으므로, `pnpm ci:full-release --sha `를 사용하여 고정된 임시 브랜치를 생성하세요. -라이브/제공자 범위를 선택하려면 `release_profile`을 사용하세요. +라이브/프로바이더 범위를 선택하려면 `release_profile`을 사용하세요. - `minimum`: 가장 빠른 릴리스 핵심 OpenAI/코어 라이브 및 Docker 경로 -- `stable`: 릴리스 승인을 위한 minimum에 stable 제공자/백엔드 커버리지 추가 -- `full`: stable에 광범위한 권고 제공자/미디어 커버리지 추가 +- `stable`: minimum에 릴리스 승인을 위한 stable 프로바이더/백엔드 커버리지 추가 +- `full`: stable에 광범위한 자문 프로바이더/미디어 커버리지 추가 -`OpenClaw Release Checks`는 신뢰할 수 있는 워크플로 ref를 사용해 대상 ref를 한 번 `release-package-under-test`로 확인하고, 해당 아티팩트를 릴리스 경로 Docker 검사와 패키지 승인 모두에서 재사용합니다. 이렇게 하면 모든 패키지 대상 박스가 동일한 바이트를 사용하고 반복적인 패키지 빌드를 피할 수 있습니다. -교차 OS OpenAI 설치 스모크는 repo/org 변수가 설정된 경우 `OPENCLAW_CROSS_OS_OPENAI_MODEL`을 사용하고, 그렇지 않으면 `openai/gpt-5.4`를 사용합니다. 이 레인은 가장 느린 기본 모델을 벤치마킹하는 것이 아니라 패키지 설치, 온보딩, Gateway 시작, 라이브 에이전트 턴 하나를 증명하기 때문입니다. 더 넓은 라이브 제공자 매트릭스는 모델별 커버리지를 위한 위치로 유지됩니다. +`OpenClaw Release Checks`는 신뢰된 워크플로 ref를 사용하여 대상 ref를 한 번 `release-package-under-test`로 해석하고, 릴리스 경로 Docker 검사와 Package Acceptance 모두에서 해당 아티팩트를 재사용합니다. 이렇게 하면 모든 패키지 대상 박스가 동일한 바이트를 사용하고 반복적인 패키지 빌드를 피할 수 있습니다. +교차 OS OpenAI 설치 스모크는 repo/org 변수가 설정된 경우 `OPENCLAW_CROSS_OS_OPENAI_MODEL`을 사용하고, 그렇지 않으면 `openai/gpt-5.4`를 사용합니다. 이 레인은 가장 느린 기본 모델을 벤치마킹하는 것이 아니라 패키지 설치, 온보딩, gateway 시작, 라이브 agent 턴 1회를 증명하기 때문입니다. 더 넓은 라이브 프로바이더 매트릭스는 모델별 커버리지를 위한 위치로 유지됩니다. 릴리스 단계에 따라 다음 변형을 사용하세요. @@ -218,22 +326,23 @@ gh workflow run full-release-validation.yml \ -f npm_telegram_provider_mode=mock-openai ``` -집중 수정 후 첫 재실행으로 전체 엄브렐라를 사용하지 마세요. 하나의 박스가 실패하면 다음 증명에는 실패한 자식 워크플로, 작업, Docker 레인, 패키지 프로필, 모델 제공자, 또는 QA 레인을 사용하세요. 수정이 공유 릴리스 오케스트레이션을 변경했거나 이전의 전체 박스 증거를 오래된 것으로 만든 경우에만 전체 엄브렐라를 다시 실행하세요. 엄브렐라의 최종 검증기는 기록된 자식 워크플로 실행 ID를 다시 확인하므로, 자식 워크플로가 성공적으로 재실행된 후에는 실패한 부모 `Verify full validation` 작업만 재실행하세요. +집중 수정 후 첫 재실행으로 전체 엄브렐라를 사용하지 마세요. 하나의 박스가 실패하면 다음 증명에는 실패한 하위 워크플로, 작업, Docker 레인, 패키지 프로필, 모델 프로바이더 또는 QA 레인을 사용하세요. 수정이 공유 릴리스 오케스트레이션을 변경했거나 이전의 전체 박스 증거를 더 이상 신뢰할 수 없게 만든 경우에만 전체 엄브렐라를 다시 실행하세요. 엄브렐라의 최종 검증자는 기록된 하위 워크플로 실행 ID를 다시 확인하므로, 하위 워크플로가 성공적으로 재실행된 후에는 실패한 상위 `Verify full validation` 작업만 다시 실행하세요. -제한된 복구에는 엄브렐라에 `rerun_group`을 전달하세요. `all`은 실제 릴리스 후보 실행이고, `ci`는 일반 CI 자식만 실행하며, `plugin-prerelease`는 릴리스 전용 Plugin 자식만 실행하고, `release-checks`는 모든 릴리스 박스를 실행합니다. 더 좁은 릴리스 그룹은 `install-smoke`, `cross-os`, `live-e2e`, `package`, `qa`, `qa-parity`, `qa-live`, `npm-telegram`입니다. 집중 `npm-telegram` 재실행에는 `npm_telegram_package_spec`이 필요합니다. `release_profile=full`인 full/all 실행은 release-checks 패키지 아티팩트를 사용합니다. +제한된 복구에는 엄브렐라에 `rerun_group`을 전달하세요. `all`은 실제 릴리스 후보 실행이고, `ci`는 일반 CI 하위 항목만 실행하며, `plugin-prerelease`는 릴리스 전용 Plugin 하위 항목만 실행하고, `release-checks`는 모든 릴리스 박스를 실행합니다. 더 좁은 릴리스 그룹은 `install-smoke`, `cross-os`, `live-e2e`, `package`, `qa`, `qa-parity`, `qa-live`, `npm-telegram`입니다. +집중 `npm-telegram` 재실행에는 `npm_telegram_package_spec`이 필요합니다. `release_profile=full`인 full/all 실행은 release-checks 패키지 아티팩트를 사용합니다. ### Vitest -Vitest 박스는 수동 `CI` 자식 워크플로입니다. 수동 CI는 의도적으로 변경 범위 지정을 우회하고 릴리스 후보에 대해 일반 테스트 그래프를 강제합니다. Linux Node 샤드, 번들 Plugin 샤드, 채널 계약, Node 22 호환성, `check`, `check-additional`, 빌드 스모크, 문서 검사, Python Skills, Windows, macOS, Android, Control UI i18n이 포함됩니다. +Vitest 박스는 수동 `CI` 하위 워크플로입니다. 수동 CI는 의도적으로 변경 범위 지정은 우회하고 릴리스 후보에 대해 일반 테스트 그래프를 강제합니다. Linux Node 샤드, 번들 Plugin 샤드, 채널 계약, Node 22 호환성, `check`, `check-additional`, 빌드 스모크, 문서 검사, Python skills, Windows, macOS, Android, Control UI i18n이 포함됩니다. -"소스 트리가 전체 일반 테스트 스위트를 통과했는가?"에 답하려면 이 박스를 사용하세요. 이는 릴리스 경로 제품 검증과 같지 않습니다. 보관할 증거: +이 박스는 "소스 트리가 전체 일반 테스트 스위트를 통과했는가?"에 답하는 데 사용하세요. 릴리스 경로 제품 검증과는 다릅니다. 보관할 증거: - 디스패치된 `CI` 실행 URL을 보여주는 `Full Release Validation` 요약 -- 정확한 대상 SHA에서 성공한 `CI` 실행 -- 회귀를 조사할 때 CI 작업에서 실패했거나 느린 샤드 이름 -- 실행에 성능 분석이 필요할 때 `.artifacts/vitest-shard-timings.json` 같은 Vitest 타이밍 아티팩트 +- 정확한 대상 SHA에서 초록색인 `CI` 실행 +- 회귀를 조사할 때 CI 작업의 실패했거나 느린 샤드 이름 +- 실행에 성능 분석이 필요한 경우 `.artifacts/vitest-shard-timings.json` 같은 Vitest 타이밍 아티팩트 -릴리스에 결정론적인 일반 CI가 필요하지만 Docker, QA Lab, 라이브, 교차 OS, 또는 패키지 박스가 필요하지 않은 경우에만 수동 CI를 직접 실행하세요. +릴리스에 결정론적인 일반 CI가 필요하지만 Docker, QA Lab, 라이브, 교차 OS 또는 패키지 박스는 필요하지 않은 경우에만 수동 CI를 직접 실행하세요. ```bash gh workflow run ci.yml --ref main -f target_ref=release/YYYY.M.D @@ -241,51 +350,52 @@ gh workflow run ci.yml --ref main -f target_ref=release/YYYY.M.D ### Docker -Docker 박스는 `openclaw-live-and-e2e-checks-reusable.yml`과 릴리스 모드 `install-smoke` 워크플로를 통해 `OpenClaw Release Checks` 안에 있습니다. 이는 소스 수준 테스트만이 아니라 패키징된 Docker 환경을 통해 릴리스 후보를 검증합니다. +Docker 박스는 `openclaw-live-and-e2e-checks-reusable.yml`을 통해 `OpenClaw Release Checks` 안에 있으며, 릴리스 모드 `install-smoke` 워크플로도 포함됩니다. 소스 수준 테스트만이 아니라 패키징된 Docker 환경을 통해 릴리스 후보를 검증합니다. 릴리스 Docker 커버리지에는 다음이 포함됩니다. - 느린 Bun 전역 설치 스모크가 활성화된 전체 설치 스모크 -- 대상 SHA별 루트 Dockerfile 스모크 이미지 준비/재사용, QR, root/gateway, installer/Bun 스모크 작업은 별도 install-smoke 샤드로 실행 +- 대상 SHA별 루트 Dockerfile 스모크 이미지 준비/재사용. QR, 루트/gateway, 설치 프로그램/Bun 스모크 작업은 별도 install-smoke 샤드로 실행 - 저장소 E2E 레인 - 릴리스 경로 Docker 청크: `core`, `package-update-openai`, `package-update-anthropic`, `package-update-core`, `plugins-runtime-plugins`, `plugins-runtime-services`, `plugins-runtime-install-a`, `plugins-runtime-install-b`, `plugins-runtime-install-c`, `plugins-runtime-install-d`, `plugins-runtime-install-e`, `plugins-runtime-install-f`, `plugins-runtime-install-g`, `plugins-runtime-install-h` -- 요청 시 `plugins-runtime-services` 청크 내부의 OpenWebUI 커버리지 +- 요청된 경우 `plugins-runtime-services` 청크 내부의 OpenWebUI 커버리지 - `bundled-plugin-install-uninstall-0`부터 `bundled-plugin-install-uninstall-23`까지 분할된 번들 Plugin 설치/제거 레인 -- 릴리스 검사가 라이브 스위트를 포함할 때 라이브/E2E 제공자 스위트 및 Docker 라이브 모델 커버리지 +- 릴리스 검사가 라이브 스위트를 포함할 때 라이브/E2E 프로바이더 스위트 및 Docker 라이브 모델 커버리지 -재실행하기 전에 Docker 아티팩트를 사용하세요. 릴리스 경로 스케줄러는 레인 로그, `summary.json`, `failures.json`, 단계 타이밍, 스케줄러 계획 JSON, 재실행 명령이 포함된 `.artifacts/docker-tests/`를 업로드합니다. 집중 복구에는 모든 릴리스 청크를 재실행하는 대신 재사용 가능한 라이브/E2E 워크플로에서 `docker_lanes=`을 사용하세요. 생성된 재실행 명령에는 가능한 경우 이전 `package_artifact_run_id`와 준비된 Docker 이미지 입력이 포함되므로 실패한 레인이 동일한 tarball과 GHCR 이미지를 재사용할 수 있습니다. +재실행하기 전에 Docker 아티팩트를 사용하세요. 릴리스 경로 스케줄러는 레인 로그, `summary.json`, `failures.json`, 단계 타이밍, 스케줄러 계획 JSON, 재실행 명령이 포함된 `.artifacts/docker-tests/`를 업로드합니다. 집중 복구에는 모든 릴리스 청크를 다시 실행하는 대신 재사용 가능한 live/E2E 워크플로에서 `docker_lanes=`을 사용하세요. 생성된 재실행 명령에는 사용 가능한 경우 이전 `package_artifact_run_id`와 준비된 Docker 이미지 입력이 포함되므로, 실패한 레인이 동일한 tarball과 GHCR 이미지를 재사용할 수 있습니다. ### QA Lab -QA Lab 박스도 `OpenClaw Release Checks`의 일부입니다. 이는 Vitest 및 Docker 패키지 메커니즘과 별개인 에이전트 동작 및 채널 수준 릴리스 게이트입니다. +QA Lab 박스도 `OpenClaw Release Checks`의 일부입니다. Vitest 및 Docker 패키지 메커니즘과 별개인 agentic 동작 및 채널 수준 릴리스 게이트입니다. 릴리스 QA Lab 커버리지에는 다음이 포함됩니다. -- 에이전트 패리티 팩을 사용해 OpenAI 후보 레인을 Opus 4.6 기준선과 비교하는 mock 패리티 레인 +- agentic 패리티 팩을 사용해 OpenAI 후보 레인을 Opus 4.6 기준선과 비교하는 mock 패리티 레인 - `qa-live-shared` 환경을 사용하는 빠른 라이브 Matrix QA 프로필 - Convex CI 자격 증명 임대를 사용하는 라이브 Telegram QA 레인 -- 릴리스 텔레메트리에 명시적 로컬 증명이 필요할 때 `pnpm qa:otel:smoke` +- 릴리스 텔레메트리에 명시적 로컬 증명이 필요한 경우 `pnpm qa:otel:smoke` -"릴리스가 QA 시나리오와 라이브 채널 흐름에서 올바르게 동작하는가?"에 답하려면 이 박스를 사용하세요. 릴리스를 승인할 때 패리티, Matrix, Telegram 레인의 아티팩트 URL을 보관하세요. 전체 Matrix 커버리지는 기본 릴리스 핵심 레인이 아니라 수동 샤딩 QA-Lab 실행으로 계속 사용할 수 있습니다. +이 박스는 "릴리스가 QA 시나리오와 라이브 채널 흐름에서 올바르게 동작하는가?"에 답하는 데 사용하세요. 릴리스를 승인할 때 패리티, Matrix, Telegram 레인의 아티팩트 URL을 보관하세요. 전체 Matrix 커버리지는 기본 릴리스 핵심 레인이 아니라 수동 샤딩 QA-Lab 실행으로 계속 사용할 수 있습니다. ### 패키지 -패키지 박스는 설치 가능한 제품 게이트입니다. 이는 `Package Acceptance`와 resolver `scripts/resolve-openclaw-package-candidate.mjs`를 기반으로 합니다. resolver는 후보를 Docker E2E가 소비하는 `package-under-test` tarball로 정규화하고, 패키지 인벤토리를 검증하며, 패키지 버전과 SHA-256을 기록하고, 워크플로 하네스 ref를 패키지 소스 ref와 분리해 유지합니다. +Package 박스는 설치 가능한 제품 게이트입니다. `Package Acceptance`와 해석기 `scripts/resolve-openclaw-package-candidate.mjs`가 뒷받침합니다. 해석기는 후보를 Docker E2E가 소비하는 `package-under-test` tarball로 정규화하고, 패키지 인벤토리를 검증하며, 패키지 버전과 SHA-256을 기록하고, 워크플로 하네스 ref를 패키지 소스 ref와 분리해 유지합니다. 지원되는 후보 소스: - `source=npm`: `openclaw@beta`, `openclaw@latest`, 또는 정확한 OpenClaw 릴리스 버전 -- `source=ref`: 선택한 `workflow_ref` 하네스로 신뢰할 수 있는 `package_ref` 브랜치, 태그, 또는 전체 커밋 SHA를 패킹 +- `source=ref`: 선택한 `workflow_ref` 하네스로 신뢰된 `package_ref` 브랜치, 태그 또는 전체 커밋 SHA를 패키징 - `source=url`: 필수 `package_sha256`이 있는 HTTPS `.tgz` 다운로드 - `source=artifact`: 다른 GitHub Actions 실행에서 업로드한 `.tgz` 재사용 -`OpenClaw Release Checks`는 `source=artifact`, 준비된 릴리스 패키지 아티팩트, `suite_profile=custom`, `docker_lanes=doctor-switch update-channel-switch upgrade-survivor published-upgrade-survivor plugins-offline plugin-update`, `published_upgrade_survivor_baselines=all-since-2026.4.23`, `published_upgrade_survivor_scenarios=reported-issues`, `telegram_mode=mock-openai`로 패키지 승인을 실행합니다. 패키지 승인은 마이그레이션, 업데이트, 오래된 Plugin 종속성 정리, 오프라인 Plugin 픽스처, Plugin 업데이트, Telegram 패키지 QA를 동일하게 확인된 tarball에 대해 유지합니다. 업그레이드 매트릭스는 `2026.4.23`부터 `latest`까지 npm에 게시된 모든 stable 기준선을 포괄합니다. 이미 출시된 후보에는 `source=npm`으로 패키지 승인을 사용하고, 게시 전 SHA 기반 로컬 npm tarball에는 `source=ref`/`source=artifact`를 사용하세요. 이는 이전에 Parallels가 필요했던 대부분의 패키지/업데이트 커버리지를 대체하는 GitHub 네이티브 방식입니다. 교차 OS 릴리스 검사는 OS별 온보딩, 설치 관리자, 플랫폼 동작에 여전히 중요하지만, 패키지/업데이트 제품 검증은 패키지 승인을 우선해야 합니다. +`OpenClaw Release Checks`는 `source=artifact`, 준비된 릴리스 패키지 아티팩트, `suite_profile=custom`, `docker_lanes=doctor-switch update-channel-switch upgrade-survivor published-upgrade-survivor plugins-offline plugin-update`, `published_upgrade_survivor_baselines=all-since-2026.4.23`, `published_upgrade_survivor_scenarios=reported-issues`, `telegram_mode=mock-openai`로 Package Acceptance를 실행합니다. Package Acceptance는 동일하게 해석된 tarball에 대해 마이그레이션, 업데이트, 오래된 Plugin 종속성 정리, 오프라인 Plugin fixture, Plugin 업데이트, Telegram 패키지 QA를 유지합니다. 업그레이드 매트릭스는 `2026.4.23`부터 `latest`까지 npm에 게시된 모든 stable 기준선을 포함합니다. 이미 배포된 후보에는 `source=npm`으로 Package Acceptance를 사용하고, 게시 전 SHA 기반 로컬 npm tarball에는 `source=ref`/`source=artifact`를 사용하세요. 이는 이전에 Parallels가 필요했던 대부분의 패키지/업데이트 커버리지를 대체하는 GitHub 네이티브 방식입니다. 교차 OS 릴리스 검사는 OS별 온보딩, 설치 프로그램, 플랫폼 동작에 여전히 중요하지만, 패키지/업데이트 제품 검증에는 Package Acceptance를 우선해야 합니다. -업데이트 및 Plugin 검증을 위한 표준 체크리스트는 [업데이트 및 Plugin 테스트](/ko/help/testing-updates-plugins)입니다. Plugin 설치/업데이트, doctor 정리, 또는 게시된 패키지 마이그레이션 변경을 증명하는 로컬, Docker, 패키지 승인, 또는 release-check 레인을 결정할 때 사용하세요. 모든 stable `2026.4.23+` 패키지에서 게시된 업데이트 마이그레이션을 포괄적으로 수행하는 것은 Full Release CI의 일부가 아니라 별도의 수동 `Update Migration` 워크플로입니다. +업데이트 및 Plugin 검증을 위한 표준 체크리스트는 [업데이트 및 Plugin 테스트](/ko/help/testing-updates-plugins)입니다. Plugin 설치/업데이트, doctor 정리 또는 게시된 패키지 마이그레이션 변경을 어떤 로컬, Docker, Package Acceptance 또는 release-check 레인이 증명하는지 결정할 때 사용하세요. +모든 stable `2026.4.23+` 패키지에서 수행하는 철저한 게시 업데이트 마이그레이션은 별도의 수동 `Update Migration` 워크플로이며, Full Release CI의 일부가 아닙니다. -레거시 package-acceptance 완화는 의도적으로 시간 제한이 있습니다. `2026.4.25`까지의 패키지는 이미 npm에 게시된 메타데이터 공백에 대해 호환성 경로를 사용할 수 있습니다. tarball에 누락된 private QA 인벤토리 항목, 누락된 `gateway install --wrapper`, tarball 파생 git 픽스처에 누락된 패치 파일, 누락된 영속 `update.channel`, 레거시 Plugin 설치 기록 위치, 누락된 marketplace 설치 기록 영속성, `plugins update` 중 config 메타데이터 마이그레이션이 포함됩니다. 게시된 `2026.4.26` 패키지는 이미 출시된 로컬 빌드 메타데이터 스탬프 파일에 대해 경고할 수 있습니다. 이후 패키지는 최신 패키지 계약을 충족해야 하며, 동일한 공백은 릴리스 검증에서 실패합니다. +기존 package-acceptance 관대함은 의도적으로 시간 제한이 있습니다. `2026.4.25`까지의 패키지는 npm에 이미 게시된 메타데이터 공백에 대해 호환성 경로를 사용할 수 있습니다. tarball에서 누락된 비공개 QA 인벤토리 항목, 누락된 `gateway install --wrapper`, tarball에서 파생된 git fixture의 누락된 패치 파일, 누락된 영구 `update.channel`, 기존 Plugin 설치 기록 위치, 누락된 marketplace 설치 기록 영속성, `plugins update` 중 config 메타데이터 마이그레이션이 여기에 포함됩니다. 게시된 `2026.4.26` 패키지는 이미 배포된 로컬 빌드 메타데이터 스탬프 파일에 대해 경고할 수 있습니다. 이후 패키지는 현대적인 패키지 계약을 충족해야 하며, 동일한 공백은 릴리스 검증에 실패합니다. -릴리스 질문이 실제 설치 가능한 패키지에 관한 것이라면 더 넓은 패키지 승인 프로필을 사용하세요. +릴리스 질문이 실제 설치 가능한 패키지에 관한 것이라면 더 넓은 Package Acceptance 프로필을 사용하세요. ```bash gh workflow run package-acceptance.yml \ @@ -297,26 +407,26 @@ gh workflow run package-acceptance.yml \ -f published_upgrade_survivor_baseline=openclaw@2026.4.26 ``` -일반 패키지 프로필: +일반적인 패키지 프로필: -- `smoke`: 빠른 패키지 설치/채널/에이전트, Gateway 네트워크, 구성 - 다시 로드 레인 +- `smoke`: 빠른 패키지 설치/채널/에이전트, Gateway 네트워크 및 config + reload 레인 - `package`: 라이브 ClawHub 없이 설치/업데이트/Plugin 패키지 계약을 확인합니다. 이것이 릴리스 검사 - 기본값입니다. -- `product`: `package`에 MCP 채널, cron/subagent 정리, OpenAI 웹 - 검색, OpenWebUI를 더한 항목 + 기본값입니다 +- `product`: `package`에 MCP 채널, cron/하위 에이전트 정리, OpenAI 웹 + 검색 및 OpenWebUI를 추가합니다 - `full`: OpenWebUI가 포함된 Docker 릴리스 경로 청크 - `custom`: 집중 재실행을 위한 정확한 `docker_lanes` 목록 -패키지 후보 Telegram 증명을 위해 Package Acceptance에서 `telegram_mode=mock-openai` 또는 +패키지 후보 Telegram 검증에는 Package Acceptance에서 `telegram_mode=mock-openai` 또는 `telegram_mode=live-frontier`를 활성화하세요. 워크플로는 확인된 -`package-under-test` tarball을 Telegram 레인으로 전달합니다. 독립 실행형 -Telegram 워크플로는 게시 후 검사를 위해 게시된 npm 명세도 계속 허용합니다. +`package-under-test` 타르볼을 Telegram 레인에 전달합니다. 독립 실행형 +Telegram 워크플로는 게시 후 검사를 위해 여전히 게시된 npm 사양을 받습니다. ## 릴리스 게시 자동화 -`OpenClaw Release Publish`는 일반적인 변경 적용 게시 진입점입니다. 이 워크플로는 -릴리스에 필요한 순서대로 trusted-publisher 워크플로를 오케스트레이션합니다. +`OpenClaw Release Publish`는 일반적인 변경 게시 진입점입니다. 이 워크플로는 +릴리스에 필요한 순서대로 신뢰할 수 있는 게시자 워크플로를 오케스트레이션합니다. 1. 릴리스 태그를 체크아웃하고 해당 커밋 SHA를 확인합니다. 2. 태그가 `main` 또는 `release/*`에서 도달 가능한지 확인합니다. @@ -337,7 +447,7 @@ gh workflow run openclaw-release-publish.yml \ -f npm_dist_tag=beta ``` -기본 beta dist-tag로 안정 릴리스 게시: +기본 beta dist-tag로 안정 버전 게시: ```bash gh workflow run openclaw-release-publish.yml \ @@ -347,7 +457,7 @@ gh workflow run openclaw-release-publish.yml \ -f npm_dist_tag=beta ``` -`latest`로 직접 안정 승격하는 것은 명시적입니다. +`latest`로 직접 안정 버전을 승격하는 것은 명시적입니다. ```bash gh workflow run openclaw-release-publish.yml \ @@ -360,79 +470,81 @@ gh workflow run openclaw-release-publish.yml \ 하위 수준 `Plugin NPM Release` 및 `Plugin ClawHub Release` 워크플로는 집중 복구 또는 재게시 작업에만 사용하세요. 선택한 Plugin 복구의 경우 `plugin_publish_scope=selected` 및 `plugins=@openclaw/name`을 -`OpenClaw Release Publish`에 전달하거나, OpenClaw 패키지를 게시하면 안 되는 경우 +`OpenClaw Release Publish`에 전달하거나, OpenClaw 패키지가 게시되면 안 되는 경우 자식 워크플로를 직접 디스패치하세요. ## NPM 워크플로 입력 -`OpenClaw NPM Release`는 운영자가 제어하는 다음 입력을 허용합니다. +`OpenClaw NPM Release`는 운영자가 제어하는 다음 입력을 받습니다. -- `tag`: `v2026.4.2`, `v2026.4.2-1` 또는 - `v2026.4.2-beta.1` 같은 필수 릴리스 태그입니다. `preflight_only=true`일 때는 검증 전용 preflight를 위해 현재의 - 전체 40자 워크플로 브랜치 커밋 SHA도 사용할 수 있습니다. -- `preflight_only`: 검증/빌드/패키지만 수행하려면 `true`, 실제 게시 경로에는 `false` -- `preflight_run_id`: 실제 게시 경로에서 필요하며, 워크플로가 성공한 preflight 실행에서 준비된 tarball을 재사용하도록 합니다. -- `npm_dist_tag`: 게시 경로의 npm 대상 태그입니다. 기본값은 `beta`입니다. +- `tag`: 필수 릴리스 태그입니다. 예: `v2026.4.2`, `v2026.4.2-1` 또는 + `v2026.4.2-beta.1`. `preflight_only=true`인 경우, 검증 전용 프리플라이트를 위해 현재 + 전체 40자 워크플로 브랜치 커밋 SHA도 사용할 수 있습니다 +- `preflight_only`: 검증/빌드/패키지만 수행하려면 `true`, 실제 게시 경로는 `false` +- `preflight_run_id`: 실제 게시 경로에서 필요합니다. 워크플로가 성공한 프리플라이트 실행에서 + 준비된 타르볼을 재사용하도록 합니다 +- `npm_dist_tag`: 게시 경로의 npm 대상 태그입니다. 기본값은 `beta`입니다 -`OpenClaw Release Publish`는 운영자가 제어하는 다음 입력을 허용합니다. +`OpenClaw Release Publish`는 운영자가 제어하는 다음 입력을 받습니다. -- `tag`: 필수 릴리스 태그입니다. 이미 존재해야 합니다. -- `preflight_run_id`: 성공한 `OpenClaw NPM Release` preflight 실행 ID입니다. - `publish_openclaw_npm=true`일 때 필요합니다. +- `tag`: 필수 릴리스 태그입니다. 이미 존재해야 합니다 +- `preflight_run_id`: 성공한 `OpenClaw NPM Release` 프리플라이트 실행 ID입니다. + `publish_openclaw_npm=true`일 때 필요합니다 - `npm_dist_tag`: OpenClaw 패키지의 npm 대상 태그 - `plugin_publish_scope`: 기본값은 `all-publishable`입니다. 집중 복구 작업에만 - `selected`를 사용하세요. -- `plugins`: `plugin_publish_scope=selected`일 때 쉼표로 구분한 `@openclaw/*` 패키지 이름 -- `publish_openclaw_npm`: 기본값은 `true`입니다. 워크플로를 Plugin 전용 복구 오케스트레이터로 사용할 때만 - `false`로 설정하세요. + `selected`를 사용하세요 +- `plugins`: `plugin_publish_scope=selected`일 때 쉼표로 구분된 `@openclaw/*` 패키지 이름 +- `publish_openclaw_npm`: 기본값은 `true`입니다. 워크플로를 Plugin 전용 복구 오케스트레이터로 + 사용할 때만 `false`로 설정하세요 -`OpenClaw Release Checks`는 운영자가 제어하는 다음 입력을 허용합니다. +`OpenClaw Release Checks`는 운영자가 제어하는 다음 입력을 받습니다. -- `ref`: 검증할 브랜치, 태그 또는 전체 커밋 SHA입니다. 시크릿을 사용하는 검사는 +- `ref`: 검증할 브랜치, 태그 또는 전체 커밋 SHA입니다. 시크릿이 필요한 검사는 확인된 커밋이 OpenClaw 브랜치 또는 릴리스 태그에서 도달 가능해야 합니다. 규칙: -- 안정 및 수정 태그는 `beta` 또는 `latest` 중 하나로 게시할 수 있습니다. -- 베타 사전 릴리스 태그는 `beta`로만 게시할 수 있습니다. -- `OpenClaw NPM Release`에서 전체 커밋 SHA 입력은 `preflight_only=true`일 때만 허용됩니다. -- `OpenClaw Release Checks`와 `Full Release Validation`은 항상 검증 전용입니다. -- 실제 게시 경로는 preflight 중에 사용한 것과 동일한 `npm_dist_tag`를 사용해야 합니다. - 워크플로는 게시를 계속하기 전에 해당 메타데이터를 확인합니다. +- 안정 버전 및 수정 태그는 `beta` 또는 `latest` 중 하나로 게시할 수 있습니다 +- 베타 프리릴리스 태그는 `beta`로만 게시할 수 있습니다 +- `OpenClaw NPM Release`의 경우 전체 커밋 SHA 입력은 `preflight_only=true`일 때만 허용됩니다 +- `OpenClaw Release Checks` 및 `Full Release Validation`은 항상 검증 전용입니다 +- 실제 게시 경로는 프리플라이트 중 사용한 것과 동일한 `npm_dist_tag`를 사용해야 합니다. + 워크플로는 게시가 계속되기 전에 해당 메타데이터를 확인합니다 ## 안정 npm 릴리스 순서 안정 npm 릴리스를 만들 때: -1. `preflight_only=true`로 `OpenClaw NPM Release`를 실행합니다. - - 태그가 존재하기 전에는 preflight 워크플로의 검증 전용 드라이 런을 위해 현재 전체 워크플로 브랜치 커밋 - SHA를 사용할 수 있습니다. -2. 일반적인 beta-first 흐름에는 `npm_dist_tag=beta`를 선택하고, 의도적으로 직접 안정 게시를 원하는 경우에만 - `latest`를 선택합니다. +1. `preflight_only=true`로 `OpenClaw NPM Release`를 실행합니다 + - 태그가 존재하기 전에는 프리플라이트 워크플로의 검증 전용 드라이런을 위해 현재 전체 워크플로 브랜치 커밋 + SHA를 사용할 수 있습니다 +2. 일반적인 beta 우선 흐름에는 `npm_dist_tag=beta`를 선택하고, 직접 안정 버전 게시를 의도한 경우에만 + `latest`를 선택합니다 3. 하나의 수동 워크플로에서 일반 CI와 라이브 프롬프트 캐시, Docker, QA Lab, - Matrix, Telegram 범위를 함께 원할 때 릴리스 브랜치, 릴리스 태그 또는 전체 - 커밋 SHA에서 `Full Release Validation`을 실행합니다. + Matrix, Telegram 커버리지를 원할 때는 릴리스 브랜치, 릴리스 태그 또는 전체 + 커밋 SHA에서 `Full Release Validation`을 실행합니다 4. 결정론적인 일반 테스트 그래프만 의도적으로 필요한 경우, 대신 릴리스 ref에서 - 수동 `CI` 워크플로를 실행합니다. -5. 성공한 `preflight_run_id`를 저장합니다. + 수동 `CI` 워크플로를 실행합니다 +5. 성공한 `preflight_run_id`를 저장합니다 6. 동일한 `tag`, 동일한 `npm_dist_tag`, 저장된 `preflight_run_id`로 - `OpenClaw Release Publish`를 실행합니다. 이 워크플로는 OpenClaw npm 패키지를 승격하기 전에 외부화된 Plugin을 npm과 ClawHub에 게시합니다. -7. 릴리스가 `beta`에 올라갔다면 비공개 + `OpenClaw Release Publish`를 실행합니다. 이 워크플로는 OpenClaw npm 패키지를 승격하기 전에 + 외부화된 Plugin을 npm과 ClawHub에 게시합니다 +7. 릴리스가 `beta`에 배포된 경우, 비공개 `openclaw/releases-private/.github/workflows/openclaw-npm-dist-tags.yml` - 워크플로를 사용해 해당 안정 버전을 `beta`에서 `latest`로 승격합니다. -8. 릴리스가 의도적으로 `latest`에 직접 게시되었고 `beta`도 즉시 동일한 안정 빌드를 따라야 한다면, - 동일한 비공개 워크플로를 사용해 두 dist-tag가 모두 안정 버전을 가리키도록 하거나, 예약된 - 자체 복구 동기화가 나중에 `beta`를 이동하도록 둡니다. + 워크플로를 사용하여 해당 안정 버전을 `beta`에서 `latest`로 승격합니다 +8. 릴리스가 의도적으로 `latest`에 직접 게시되었고 `beta`도 즉시 동일한 안정 빌드를 따라야 하는 경우, + 동일한 비공개 워크플로를 사용해 두 dist-tag가 모두 안정 버전을 가리키도록 하거나, + 예약된 자가 복구 동기화가 나중에 `beta`를 이동하게 둡니다 -dist-tag 변경은 여전히 `NPM_TOKEN`이 필요하기 때문에 보안을 위해 비공개 저장소에 있으며, +dist-tag 변경은 여전히 `NPM_TOKEN`이 필요하므로 보안을 위해 비공개 저장소에 있습니다. 공개 저장소는 OIDC 전용 게시를 유지합니다. -이렇게 하면 직접 게시 경로와 beta-first 승격 경로가 모두 문서화되고 운영자가 볼 수 있습니다. +이렇게 하면 직접 게시 경로와 beta 우선 승격 경로가 모두 문서화되고 운영자에게 표시됩니다. -관리자가 로컬 npm 인증으로 대체해야 하는 경우, 모든 1Password -CLI(`op`) 명령은 전용 tmux 세션 안에서만 실행하세요. 메인 에이전트 셸에서 `op`를 -직접 호출하지 마세요. tmux 안에 두면 프롬프트, -알림, OTP 처리를 관찰할 수 있고 반복되는 호스트 알림을 방지할 수 있습니다. +유지관리자가 로컬 npm 인증으로 대체해야 하는 경우, 모든 1Password +CLI(`op`) 명령은 전용 tmux 세션 안에서만 실행하세요. 기본 에이전트 셸에서 `op`를 +직접 호출하지 마세요. tmux 안에 두면 프롬프트, 경고 및 OTP 처리를 관찰할 수 있고 +반복적인 호스트 경고를 방지할 수 있습니다. ## 공개 참조 @@ -446,8 +558,9 @@ CLI(`op`) 명령은 전용 tmux 세션 안에서만 실행하세요. 메인 에 - [`scripts/package-mac-dist.sh`](https://github.com/openclaw/openclaw/blob/main/scripts/package-mac-dist.sh) - [`scripts/make_appcast.sh`](https://github.com/openclaw/openclaw/blob/main/scripts/make_appcast.sh) -관리자는 실제 실행 지침에 -[`openclaw/maintainers/release/README.md`](https://github.com/openclaw/maintainers/blob/main/release/README.md)의 비공개 릴리스 문서를 사용합니다. +유지관리자는 실제 런북에 +[`openclaw/maintainers/release/README.md`](https://github.com/openclaw/maintainers/blob/main/release/README.md)의 +비공개 릴리스 문서를 사용합니다. ## 관련