From 16d9b0a217bb516519acc2a2e97dac27e4f5ba75 Mon Sep 17 00:00:00 2001 From: chaodu-agent Date: Sat, 11 Jul 2026 18:33:29 -0400 Subject: [PATCH 1/2] docs: canary testing guide + fork-PR preview build workflow MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Extracted from #1353 to keep that PR scoped to the Codex adapter migration. Content authored on #1353 by @vixenclawsastraagent: - docs/canary-tests.md: complete canary workflow — preview builds, safety requirements, acceptance criteria, evidence template, cleanup, and rollback drills. This guide's process was exercised end-to-end during #1353's two canary rounds. - CONTRIBUTING.md: fix the E2E section's dispatch instructions — fork contributors cannot dispatch PR Preview Build with --ref (403); a maintainer dispatches from main and the workflow resolves the PR head automatically. - README architecture diagrams: updated to show gateway adapters (standalone or embedded unified), WeCom/MS Teams, and the local vs AgentCore runtime boundary. Co-authored-by: vixenclawsastraagent <268079781+vixenclawsastraagent@users.noreply.github.com> --- CONTRIBUTING.md | 11 +- README.md | 53 +++-- README.zh-TW.md | 53 +++-- docs/canary-tests.md | 548 +++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 621 insertions(+), 44 deletions(-) create mode 100644 docs/canary-tests.md diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index bd29f6024..25511fd25 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -147,13 +147,16 @@ Command::new("/usr/local/bin/agy") ### E2E testing PRs -Use the PR Preview Build workflow for fast iteration: +Use the PR Preview Build workflow for fast iteration. For the complete +workflow, safety requirements, acceptance criteria, evidence template, and +cleanup steps, see [Canary Testing Pull Requests](/docs/canary-tests.md). ```bash # 1. Push code to PR branch -# 2. Build the image -gh workflow run "PR Preview Build" --repo openabdev/openab \ - --ref -f pr_number= -f variant= +# 2. A maintainer builds the image from the upstream workflow on main. +# The workflow resolves and checks out the PR head automatically. +gh workflow run pr-preview.yml --repo openabdev/openab \ + --ref main -f pr_number= -f variant= # 3. Wait for build gh run view --repo openabdev/openab --json conclusion -q .conclusion diff --git a/README.md b/README.md index c927e9f2f..6c6341ec4 100644 --- a/README.md +++ b/README.md @@ -6,32 +6,45 @@ English | [繁體中文](README.zh-TW.md) ![OpenAB banner](images/banner.jpg) -A lightweight, secure, cloud-native ACP harness that bridges **Discord, Slack**, and any [Agent Client Protocol](https://github.com/anthropics/agent-protocol)-compatible coding CLI (Kiro CLI, Claude Code, Codex, Gemini, OpenCode, MiMo-Code, Copilot CLI, Hermes, Grok Build, Devin, Antigravity, Pi, etc.) over stdio JSON-RPC — delivering the next-generation development experience. **Telegram, LINE, Feishu/Lark, Google Chat**, and other webhook-based platforms are supported via the standalone [Custom Gateway](crates/openab-gateway/). +A lightweight, secure, cloud-native ACP harness that bridges **Discord, Slack**, and any [Agent Client Protocol](https://github.com/anthropics/agent-protocol)-compatible coding CLI (Kiro CLI, Claude Code, Codex, Gemini, OpenCode, MiMo-Code, Copilot CLI, Hermes, Grok Build, Devin, Antigravity, Pi, etc.) over stdio JSON-RPC — delivering the next-generation development experience. **Telegram, LINE, Feishu/Lark, Google Chat, WeCom, and Microsoft Teams** are available through gateway adapters, either compiled into the unified binary or deployed as the standalone [Custom Gateway](crates/openab-gateway/). 🪼 **Join our community!** Come say hi on Discord — we'd love to have you: **[🪼 OpenAB — Official](https://openab.dev/discord)** 🎉 ``` -┌──────────────┐ Gateway WS ┌──────────────┐ ACP stdio ┌──────────────────┐ -│ Discord │◄─────────────►│ │──────────────►│ coding CLI │ -│ User │ │ openab │◄── JSON-RPC ──│ (acp mode) │ -├──────────────┤ Socket Mode │ (Rust) │ ├──────────────────┤ -│ Slack │◄─────────────►│ │ │ kiro-cli acp │ -│ User │ └──────┬───────┘ │ claude-agent-acp │ -├──────────────┤ │ WebSocket │ codex-acp │ -│ Telegram │◄──webhook──┐ │ (outbound) │ gemini --acp │ -│ User │ │ │ │ copilot --acp │ -├──────────────┤ ▼ ▼ │ hermes-acp │ -│ LINE │◄──webhook──┌──────────────────┐ │ opencode acp │ -│ User │ │ Custom Gateway │ │ mimo acp │ -├──────────────┤ │ (standalone) │ │ grok agent stdio │ -│ Feishu/Lark │◄───WS──────│ │ │ devin acp │ -│ User │ │ │ │ agy-acp │ -├──────────────┤ │ │ │ pi-acp │ -│ Google Chat │◄──webhook──│ │ └──────────────────┘ -│ User │ └──────────────────┘ +┌──────────────┐ Gateway WS ┌─────────────────┐ ACP stdio ┌─────────────────────────┐ +│ Discord │◄────────────►│ │─────────────►│ agent runtime │ +│ User │ │ openab (Rust) │◄──JSON-RPC──│ (ACP process) │ +├──────────────┤ Socket Mode │ thin ACP broker │ ├─────────────────────────┤ +│ Slack │◄────────────►│ │ │ kiro-cli acp │ +│ User │ └────────┬────────┘ │ claude-agent-acp │ +├──────────────┤ ▲ │ codex-acp │ +│ Telegram │◄webhook/API─►┐ │ │ └─ Codex app-server │ +│ User │ │ │ WebSocket │ gemini --acp │ +├──────────────┤ │ │ (standalone) │ copilot --acp │ +│ LINE │◄webhook/API─►┤ │ or in-process │ cursor-agent acp │ +│ User │ │ │ (unified) │ hermes-acp │ +├──────────────┤ │ ┌──────┴───────────┐ │ opencode acp │ +│ Feishu/Lark │◄─WS/webhook─►┼►│ gateway adapters │ │ mimo acp │ +│ User │ │ │ standalone or │ │ grok agent stdio │ +├──────────────┤ │ │ embedded unified │ │ devin acp │ +│ Google Chat │◄webhook/API─►┤ └──────────────────┘ │ agy-acp │ +│ User │ │ │ pi-acp │ +├──────────────┤ │ │ openab-agent │ +│ WeCom │◄webhook/API─►┤ │ agentcore-acp │ +│ User │ │ │ └─ AWS AgentCore │ +├──────────────┤ │ │ custom ACP agent │ +│ MS Teams │◄webhook/API─►┘ └─────────────────────────┘ +│ User │ └──────────────┘ ``` +OpenAB remains a thin broker: platform adapters converge on the same +dispatcher and session pool, then cross one ACP stdio boundary to the selected +agent runtime. Gateway adapters can run as a standalone companion or be +embedded in a unified build without changing that boundary. Platform-edge +labels distinguish inbound delivery from outbound replies: `webhook/API` for +webhook platforms and `WS/webhook` for Feishu/Lark. + ## Demo ![openab demo](images/demo.png) @@ -39,7 +52,7 @@ A lightweight, secure, cloud-native ACP harness that bridges **Discord, Slack**, ## Features - **Multi-platform** — supports Discord and Slack, run one or both simultaneously -- **Custom Gateway** — extend to Telegram, LINE, Feishu/Lark, Google Chat, MS Teams via standalone [gateway](crates/openab-gateway/) +- **Gateway adapters** — extend to Telegram, LINE, Feishu/Lark, Google Chat, WeCom, and Microsoft Teams through the standalone [gateway](crates/openab-gateway/) or an opt-in unified build - **Pluggable agent backend** — swap between Kiro CLI, Claude Code, Codex, Gemini, OpenCode, MiMo-Code, Copilot CLI, Hermes, Grok Build, Devin, Antigravity, Pi via config - **@mention trigger** — mention the bot in an allowed channel to start a conversation - **Thread-based multi-turn** — auto-creates threads; no @mention needed for follow-ups diff --git a/README.zh-TW.md b/README.zh-TW.md index 1db0ed09b..36a49a71b 100644 --- a/README.zh-TW.md +++ b/README.zh-TW.md @@ -6,32 +6,45 @@ ![OpenAB banner](images/banner.jpg) -一個輕量、安全、雲端原生的 ACP harness,透過 stdio JSON-RPC 將 **Discord、Slack** 與任何相容於 [Agent Client Protocol](https://github.com/anthropics/agent-protocol) 的程式開發 CLI(Kiro CLI、Claude Code、Codex、Gemini、OpenCode、MiMo-Code、Copilot CLI、Hermes、Grok Build、Devin、Antigravity、Pi 等)連接起來,帶來新一代的開發體驗。**Telegram、LINE、Feishu/Lark、Google Chat** 以及其他以 webhook 為基礎的平台,則透過獨立的 [Custom Gateway](crates/openab-gateway/) 支援。 +一個輕量、安全、雲端原生的 ACP harness,透過 stdio JSON-RPC 將 **Discord、Slack** 與任何相容於 [Agent Client Protocol](https://github.com/anthropics/agent-protocol) 的程式開發 CLI(Kiro CLI、Claude Code、Codex、Gemini、OpenCode、MiMo-Code、Copilot CLI、Hermes、Grok Build、Devin、Antigravity、Pi 等)連接起來,帶來新一代的開發體驗。**Telegram、LINE、Feishu/Lark、Google Chat、WeCom 與 Microsoft Teams** 則由 gateway adapters 支援;可將 adapters 編入 unified binary,或部署為獨立的 [Custom Gateway](crates/openab-gateway/)。 🪼 **加入我們的社群!** 歡迎到 Discord 和大家打招呼:**[🪼 OpenAB — Official](https://openab.dev/discord)** 🎉 ``` -┌──────────────┐ Gateway WS ┌──────────────┐ ACP stdio ┌──────────────────┐ -│ Discord │◄─────────────►│ │──────────────►│ coding CLI │ -│ User │ │ openab │◄── JSON-RPC ──│ (acp mode) │ -├──────────────┤ Socket Mode │ (Rust) │ ├──────────────────┤ -│ Slack │◄─────────────►│ │ │ kiro-cli acp │ -│ User │ └──────┬───────┘ │ claude-agent-acp │ -├──────────────┤ │ WebSocket │ codex-acp │ -│ Telegram │◄──webhook──┐ │ (outbound) │ gemini --acp │ -│ User │ │ │ │ copilot --acp │ -├──────────────┤ ▼ ▼ │ hermes-acp │ -│ LINE │◄──webhook──┌──────────────────┐ │ opencode acp │ -│ User │ │ Custom Gateway │ │ mimo acp │ -├──────────────┤ │ (standalone) │ │ grok agent stdio │ -│ Feishu/Lark │◄───WS──────│ │ │ devin acp │ -│ User │ │ │ │ agy-acp │ -├──────────────┤ │ │ │ pi-acp │ -│ Google Chat │◄──webhook──│ │ └──────────────────┘ -│ User │ └──────────────────┘ +┌──────────────┐ Gateway WS ┌─────────────────┐ ACP stdio ┌─────────────────────────┐ +│ Discord │◄────────────►│ │─────────────►│ agent runtime │ +│ User │ │ openab (Rust) │◄──JSON-RPC──│ (ACP process) │ +├──────────────┤ Socket Mode │ thin ACP broker │ ├─────────────────────────┤ +│ Slack │◄────────────►│ │ │ kiro-cli acp │ +│ User │ └────────┬────────┘ │ claude-agent-acp │ +├──────────────┤ ▲ │ codex-acp │ +│ Telegram │◄webhook/API─►┐ │ │ └─ Codex app-server │ +│ User │ │ │ WebSocket │ gemini --acp │ +├──────────────┤ │ │ (standalone) │ copilot --acp │ +│ LINE │◄webhook/API─►┤ │ or in-process │ cursor-agent acp │ +│ User │ │ │ (unified) │ hermes-acp │ +├──────────────┤ │ ┌──────┴───────────┐ │ opencode acp │ +│ Feishu/Lark │◄─WS/webhook─►┼►│ gateway adapters │ │ mimo acp │ +│ User │ │ │ standalone or │ │ grok agent stdio │ +├──────────────┤ │ │ embedded unified │ │ devin acp │ +│ Google Chat │◄webhook/API─►┤ └──────────────────┘ │ agy-acp │ +│ User │ │ │ pi-acp │ +├──────────────┤ │ │ openab-agent │ +│ WeCom │◄webhook/API─►┤ │ agentcore-acp │ +│ User │ │ │ └─ AWS AgentCore │ +├──────────────┤ │ │ custom ACP agent │ +│ MS Teams │◄webhook/API─►┘ └─────────────────────────┘ +│ User │ └──────────────┘ ``` +OpenAB 仍是 thin broker:所有 platform adapters 都會進入同一套 +dispatcher 與 session pool,再透過單一 ACP stdio boundary 連接所選的 +agent runtime。Gateway adapters 可作為 standalone companion 執行,也可 +內嵌於 unified build,兩種部署方式都不會改變這個 boundary。Platform +edge 的 labels 會分別呈現 inbound delivery 與 outbound reply:webhook +platforms 使用 `webhook/API`,Feishu/Lark 則使用 `WS/webhook`。 + ## 示範 ![openab demo](images/demo.png) @@ -39,7 +52,7 @@ ## 功能特色 - **多平台支援** — 支援 Discord 與 Slack,可單獨或同時執行 -- **Custom Gateway** — 透過獨立的 [gateway](crates/openab-gateway/) 擴充至 Telegram、LINE、Feishu/Lark、Google Chat、MS Teams +- **Gateway adapters** — 可透過獨立的 [gateway](crates/openab-gateway/) 或 opt-in unified build 擴充至 Telegram、LINE、Feishu/Lark、Google Chat、WeCom 與 Microsoft Teams - **可替換的 agent backend** — 可透過設定在 Kiro CLI、Claude Code、Codex、Gemini、OpenCode、MiMo-Code、Copilot CLI、Hermes、Grok Build、Devin、Antigravity、Pi 之間切換 - **@mention 觸發** — 在允許的頻道中 mention bot,即可開始對話 - **以討論串進行多輪對話** — 自動建立討論串;後續訊息不需再次 @mention diff --git a/docs/canary-tests.md b/docs/canary-tests.md new file mode 100644 index 000000000..05cc0cc15 --- /dev/null +++ b/docs/canary-tests.md @@ -0,0 +1,548 @@ +# Canary Testing Pull Requests + +Use this guide to validate a pull request that changes an OpenAB container image, agent adapter, ACP behavior, authentication flow, session lifecycle, or gateway integration. A canary complements unit tests and Docker smoke tests; it does not replace them. + +The goal is to produce evidence that another contributor or maintainer can review and repeat without sharing production credentials. + +## Table of Contents + +- [Prerequisites](#prerequisites) +- [Build the Preview Image](#build-the-preview-image) +- [Layer 1: Image Inspection](#layer-1-image-inspection) +- [Layer 2: ACP Protocol Smoke](#layer-2-acp-protocol-smoke) + - [Transport Method](#transport-method) +- [Layer 3: Runtime Isolation Probe](#layer-3-runtime-isolation-probe) +- [Layer 4: Interactive Validation](#layer-4-interactive-validation) +- [Layer 5: Discord Gateway E2E](#layer-5-discord-gateway-e2e) + - [Local Docker Example](#local-docker-example) +- [Acceptance Checklist by Agent and Change Type](#acceptance-checklist-by-agent-and-change-type) +- [Report Results](#report-results) +- [Clean Up](#clean-up) +- [Post-Merge Canary](#post-merge-canary) +- [Worked Example](#worked-example) + +## Prerequisites + +Before testing, prepare: + +- the pull request number and full head commit SHA; +- the affected image variant, such as `codex`, `claude`, or `default`; +- a non-production bot or gateway identity and test channel; +- isolated credential and workspace volumes; +- the previous working image tag for rollback; +- the expected model, agent mode, tools, and session behavior. + +Never run two OpenAB instances with the same bot token. Both instances can receive and reply to the same message. + +Do not paste tokens, authentication files, or unredacted environment dumps into logs, issues, pull requests, or chat. Treat mounted files, credentials, service accounts, and network access as part of the canary's security boundary. + +Set reusable shell variables before starting. The example below uses PR `1353` and the `codex` variant; replace both values for the pull request under test: + +```bash +PR_NUMBER=1353 +VARIANT=codex +PR_HEAD="$(gh pr view "$PR_NUMBER" --repo openabdev/openab --json headRefOid --jq .headRefOid)" +IMAGE="ghcr.io/openabdev/openab:pr${PR_NUMBER}-${VARIANT}" +PLATFORM=linux/arm64 + +printf 'PR head: %s\nImage: %s\nPlatform: %s\n' "$PR_HEAD" "$IMAGE" "$PLATFORM" +``` + +For the `default` variant, use `IMAGE="ghcr.io/openabdev/openab:pr${PR_NUMBER}"` because its tag has no variant suffix. + +## Build the Preview Image + +Run the repository checks that apply to the change. For an image or adapter change, confirm both `Docker Smoke Test` and `Docker Smoke Test (Unified)` run against the exact PR head. + +[`PR Preview Build`](../.github/workflows/pr-preview.yml) is a manual upstream workflow. Dispatching it requires GitHub Actions write permission on `openabdev/openab`. A contributor without that permission should ask a maintainer to run it and attach the workflow URL to the PR. + +A maintainer dispatches the workflow from `main`; the workflow then resolves and checks out the pull request's head repository and branch: + +```bash +gh workflow run pr-preview.yml \ + --repo openabdev/openab \ + --ref main \ + -f pr_number="$PR_NUMBER" \ + -f variant="$VARIANT" +``` + +Find the run and wait for it to complete: + +```bash +gh run list \ + --repo openabdev/openab \ + --workflow pr-preview.yml \ + --limit 5 + +RUN_ID=29156240254 # replace with the run ID selected above + +gh run watch "$RUN_ID" \ + --repo openabdev/openab \ + --exit-status + +gh run view "$RUN_ID" \ + --repo openabdev/openab \ + --json conclusion,url,jobs +``` + +The `default` preview variant produces `ghcr.io/openabdev/openab:pr`. Agent variants produce `ghcr.io/openabdev/openab:pr-`. + +Confirm the pull request head did not move while the image was building: + +```bash +CURRENT_HEAD="$(gh pr view "$PR_NUMBER" --repo openabdev/openab --json headRefOid --jq .headRefOid)" +test "$CURRENT_HEAD" = "$PR_HEAD" || { + printf 'PR head changed: expected %s, found %s\n' "$PR_HEAD" "$CURRENT_HEAD" >&2 + exit 1 +} +``` + +Do not reuse an earlier preview image after a runtime-affecting PR head change. Rebuild it and record the new commit and digest. If the head moved only because of documentation, state that explicitly instead of describing the earlier image as a current-head build. + +## Layer 1: Image Inspection + +Record the immutable test target before exercising behavior: + +```text +PR: +PR head: +Image: +Digest: +Platform: +Runtime: +Baseline image: +``` + +Inspect the published multi-architecture manifest, pull the platform under test, and record the immutable digest: + +```bash +docker buildx imagetools inspect "$IMAGE" +docker pull --platform "$PLATFORM" "$IMAGE" +docker image inspect "$IMAGE" \ + --format '{{json .RepoDigests}}' +``` + +Inspect the runtime facts relevant to the PR. Examples include: + +```bash +AGENT_BINARY=codex-acp + +docker run --rm --entrypoint openab "$IMAGE" --version +docker run --rm -e AGENT_BINARY --entrypoint sh "$IMAGE" -c 'command -v "$AGENT_BINARY"' +docker run --rm --entrypoint "$AGENT_BINARY" "$IMAGE" --version +docker inspect "$IMAGE" \ + --format '{{range .Config.Env}}{{println .}}{{end}}' +``` + +Avoid printing the live container environment because it may include injected credentials. Inspect image configuration or specific non-secret values only. + +### Pass Criteria + +- The image digest and PR head are recorded. +- Expected OpenAB, adapter, and CLI versions are installed. +- The expected agent binary resolves on `PATH`. +- Relevant non-secret image defaults match the PR. + +## Layer 2: ACP Protocol Smoke + +For an ACP adapter change, use a minimal bidirectional JSON-RPC client to exercise the same stdio boundary OpenAB uses. The client may be written in any language; JavaScript is not required. A one-way shell pipeline is insufficient for multi-turn and permission tests because the adapter can send requests back to the client while a prompt is running. + +### Transport Method + +1. Start the preview container with stdin open and the agent binary as its entrypoint. Mount only isolated test credentials and a disposable workspace. The bidirectional client should spawn an equivalent command rather than pipe a fixed list of messages into it. For the Codex preview image: + + ```bash + AGENT_BINARY=codex-acp + TEST_AUTH_VOLUME=openab-canary-auth + TEST_AUTH_PATH=/home/node/.codex + TEST_WORKSPACE_VOLUME=openab-canary-workspace + + docker volume create "$TEST_AUTH_VOLUME" + docker volume create "$TEST_WORKSPACE_VOLUME" + + IMAGE_UID="$(docker run --rm --entrypoint id "$IMAGE" -u)" + IMAGE_GID="$(docker run --rm --entrypoint id "$IMAGE" -g)" + + docker run --rm --user root \ + -v "${TEST_AUTH_VOLUME}:/canary/auth" \ + -v "${TEST_WORKSPACE_VOLUME}:/canary/workspace" \ + --entrypoint chown \ + "$IMAGE" -R "${IMAGE_UID}:${IMAGE_GID}" /canary + + # Skip this login command when the test uses a test-only API key instead. + docker run --rm -it \ + -e HOME=/home/node \ + -v "${TEST_AUTH_VOLUME}:${TEST_AUTH_PATH}" \ + --entrypoint sh \ + "$IMAGE" -c '$OPENAB_AGENT_AUTH_COMMAND' + + docker run --rm -i \ + --entrypoint "$AGENT_BINARY" \ + -e HOME=/home/node \ + -v "${TEST_AUTH_VOLUME}:${TEST_AUTH_PATH}" \ + -v "${TEST_WORKSPACE_VOLUME}:/workspace" \ + "$IMAGE" + ``` + +2. Write one newline-delimited JSON-RPC message at a time to the process's stdin. Read and parse stdout one line at a time. Keep stderr separate so a log line cannot be mistaken for an ACP message. +3. Assign each request a unique numeric `id`. Match responses by `id`, while collecting notifications that have a `method` but no response `id`. +4. When the adapter sends `session/request_permission`, record the advertised options and reply using the request's `id`. Select the actual `optionId` advertised for the intended test outcome; do not assume every adapter uses the literal string `allow_always` as its option ID. +5. Use bounded waits. OpenAB allows up to 120 seconds for `session/new` and 30 seconds for ordinary control requests. Give live model prompts a separate, explicit turn timeout and send `session/cancel` when testing cancellation. +6. Consider a prompt complete only when the response matching its request ID arrives. Record its `stopReason`; streamed notifications alone do not prove the turn completed. + +The core wire sequence begins as follows: + +```json +{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":1,"clientCapabilities":{},"clientInfo":{"name":"openab-canary","version":"0.1.0"}}} +{"jsonrpc":"2.0","id":2,"method":"session/new","params":{"cwd":"/workspace","mcpServers":[]}} +``` + +Read `sessionId` and `configOptions` from the `session/new` response. Use that session ID in subsequent messages: + +```json +{"jsonrpc":"2.0","id":3,"method":"session/set_config_option","params":{"sessionId":"","configId":"model","value":""}} +{"jsonrpc":"2.0","id":4,"method":"session/prompt","params":{"sessionId":"","prompt":[{"type":"text","text":"Reply with ACP_OK."}]}} +{"jsonrpc":"2.0","method":"session/cancel","params":{"sessionId":""}} +``` + +If the adapter requests permission with request ID `91`, reply with the exact `optionId` selected from that request's `options` array: + +```json +{"jsonrpc":"2.0","id":91,"result":{"outcome":{"outcome":"selected","optionId":""}}} +``` + +`session/cancel` is a notification and therefore has no request ID. To test resume behavior, initialize a fresh adapter process and send: + +```json +{"jsonrpc":"2.0","id":5,"method":"session/load","params":{"sessionId":"","cwd":"/workspace","mcpServers":[]}} +``` + +Send one intentionally invalid method and require a JSON-RPC error response with the same request ID: + +```json +{"jsonrpc":"2.0","id":6,"method":"canary/invalid_method","params":{}} +``` + +Cover the methods supported by the adapter: + +1. `initialize` +2. `session/new` +3. model and configuration selection +4. a text-only `session/prompt` +5. a second prompt in the same session +6. `session/load` or resume +7. `session/cancel` +8. an intentional invalid or unavailable operation to verify actionable error propagation + +For a model or runtime migration, select the model that originally exposed the problem and require a completed turn, not only successful initialization. + +Record the request sequence, stop reason, relevant adapter events, and a redacted log excerpt. If a temporary client is used, include its source or a minimal reproducible snippet in the PR comment. + +### Pass Criteria + +- ACP initialization returns valid agent information. +- A new session advertises the expected configuration values. +- Two consecutive turns complete in the same session. +- Session loading and cancellation behave as documented. +- Errors are actionable at the OpenAB boundary. +- The target model completes a live turn when model compatibility is in scope. + +## Layer 3: Runtime Isolation Probe + +Test configuration values advertised by the adapter rather than assuming mode names are shared by every agent. For each applicable mode, record: + +- the selected value reported by the session; +- whether a permission request was emitted; +- the response selected by the client; +- whether reads, workspace writes, shell commands, and network access matched the documented policy; +- whether the workspace changed. + +When an image supplies a default, test both an unconfigured session and every supported explicit value. Explicit user configuration should override the image default. + +Do not classify a sandbox bootstrap error as a permission-policy result. First determine whether the runtime could create the requested sandbox at all. + +For Codex on Linux, these probes can distinguish an ACP permission-flow problem from unavailable user namespaces: + +```bash +docker run --rm --entrypoint codex "$IMAGE" sandbox linux /bin/true +docker run --rm --entrypoint sh "$IMAGE" -c 'if command -v bwrap >/dev/null; then exec bwrap --unshare-user --uid 0 --gid 0 /bin/true; else echo "bwrap is not installed"; exit 127; fi' +docker run --rm --entrypoint sh "$IMAGE" -c 'cat /proc/sys/kernel/unprivileged_userns_clone 2>/dev/null || echo "kernel setting unavailable"' +``` + +Run the `bwrap` command only when the binary is installed. If both commands fail before a turn begins, record the container runtime and kernel error. This is evidence below the ACP adapter boundary, not proof that the adapter rejected a permission response. + +### Pass Criteria + +- The runtime can establish the sandbox required by each tested mode, or the unsupported boundary is identified and documented. +- Read, write, approval, and network behavior matches the selected policy. +- An explicit supported mode overrides any image default. +- A denied operation leaves protected state unchanged. + +## Layer 4: Interactive Validation + +When a scripted result is ambiguous, repeat the operation in the agent's interactive CLI. Read each permission prompt and choose the response manually. This removes a pre-programmed ACP permission response as the only explanation for the result. + +Reuse the same isolated credentials and workspace. For the Codex preview image: + +```bash +docker run --rm -it \ + --entrypoint codex \ + -e HOME=/home/node \ + -v openab-canary-auth:/home/node/.codex \ + -v openab-canary-workspace:/workspace \ + -w /workspace \ + "$IMAGE" +``` + +Use natural-language requests that exercise the same behavior as the protocol canary: + +- list and read a known workspace file; +- create or modify a disposable file; +- run a harmless shell command; +- retry an operation after a permission prompt; +- cancel a running task. + +Record the command, selected mode and approval policy, human response, final result, and whether the workspace changed. + +### Pass Criteria + +- Human-selected permission responses reach the runtime. +- The result matches the selected mode and approval policy. +- A failure reproduced outside ACP is classified below the adapter boundary. +- A failure unique to ACP remains an adapter or client investigation item. + +## Layer 5: Discord Gateway E2E + +Deploy the preview image to one isolated OpenAB agent. The evidence should include: + +- preview image tag and digest; +- deployment or task identifier; +- OpenAB startup and ACP initialization; +- two consecutive user turns through the Discord gateway; +- one relevant tool or file operation; +- cancellation and actionable error behavior; +- restart followed by session resume when persistence is affected; +- absence of the original error in redacted OpenAB logs. + +Do not expand beyond one canary agent until its acceptance criteria pass. + +### Local Docker Example + +The following Codex example uses a dedicated Discord bot, one allowlisted channel and user, isolated named volumes, and a config file outside the repository. Substitute the IDs and model, and never commit either temporary file. + +Create `openab-canary.toml`: + +```toml +[discord] +bot_token = "${DISCORD_BOT_TOKEN}" +allow_all_channels = false +allow_all_users = false +allowed_channels = [""] +allowed_users = [""] + +[agent] +working_dir = "/workspace" + +[pool] +default_config_options = { model = "gpt-5.6-sol" } +``` + +Create a mode-`600` env file without placing the token directly in shell history: + +```bash +CANARY_NAME=openab-canary +CONFIG_FILE="$(pwd)/openab-canary.toml" +ENV_FILE="$(pwd)/.openab-canary.env" +AGENT_HOME=/home/node +AUTH_PATH=/home/node/.codex + +umask 077 +read -rsp 'Discord bot token: ' DISCORD_BOT_TOKEN +printf '\nDISCORD_BOT_TOKEN=%s\n' "$DISCORD_BOT_TOKEN" > "$ENV_FILE" +unset DISCORD_BOT_TOKEN +chmod 600 "$ENV_FILE" +``` + +Create isolated volumes and assign them to the image user before authentication. This prevents a root-owned volume root from blocking an unprivileged agent from creating its state files: + +```bash +docker volume create openab-canary-auth +docker volume create openab-canary-state +docker volume create openab-canary-workspace + +IMAGE_UID="$(docker run --rm --entrypoint id "$IMAGE" -u)" +IMAGE_GID="$(docker run --rm --entrypoint id "$IMAGE" -g)" + +docker run --rm --user root \ + -v openab-canary-auth:/canary/auth \ + -v openab-canary-state:/canary/state \ + -v openab-canary-workspace:/canary/workspace \ + --entrypoint chown \ + "$IMAGE" -R "${IMAGE_UID}:${IMAGE_GID}" /canary +``` + +Authenticate the isolated agent volume when the image does not already use an API key supplied through a test-only secret: + +```bash +docker run --rm -it \ + -e HOME="$AGENT_HOME" \ + -v "openab-canary-auth:${AUTH_PATH}" \ + --entrypoint sh \ + "$IMAGE" -c '$OPENAB_AGENT_AUTH_COMMAND' +``` + +Start one local canary instance and inspect its startup without printing the env file: + +```bash +docker run -d \ + --name "$CANARY_NAME" \ + --env-file "$ENV_FILE" \ + -e HOME="$AGENT_HOME" \ + -v "$CONFIG_FILE:/etc/openab/config.toml:ro" \ + -v "openab-canary-auth:${AUTH_PATH}" \ + -v "openab-canary-state:${AGENT_HOME}/.openab" \ + -v openab-canary-workspace:/workspace \ + "$IMAGE" + +docker ps --filter "name=${CANARY_NAME}" +docker logs --since 5m "$CANARY_NAME" +``` + +Exercise the gateway as a user rather than replaying fixed requests: mention the bot in the allowlisted channel, complete two turns in the created thread, inspect `/models`, run one harmless tool operation, start a long-running prompt and invoke `/cancel` while it is active, then restart the container and send another message in the same thread. Correlate every result with the OpenAB logs. + +```bash +docker restart "$CANARY_NAME" +docker logs --since 5m "$CANARY_NAME" +``` + +Scan for the original regression and new initialization or turn failures before reporting success: + +```bash +if docker logs "$CANARY_NAME" 2>&1 | rg -i 'ACP_TURN_FAILED|model.*requires a newer version|panicked|\b(ERROR|WARN)\b'; then + echo 'Review the matched canary errors before proceeding.' >&2 + exit 1 +fi +``` + +The Discord response after restart proves message delivery only when the logs also show that the same thread and ACP session were restored through `session/load`. Likewise, a `/cancel` acknowledgement proves command routing only when the in-flight turn also ends with the expected cancellation result. + +### Pass Criteria + +- The test bot receives and replies to messages once, without duplication. +- The expected agent, model, and mode are visible in the session. +- Multi-turn context, tools, cancellation, and errors cross the gateway intact. +- Session state resumes after restart when persistence is in scope. +- No original regression or new initialization failure appears in logs. + +## Acceptance Checklist by Agent and Change Type + +Select the rows that apply. Mark other rows `Not applicable`. + +| Change type | Required evidence | +|------------|-------------------| +| Container or dependency | Image digest, binary path, runtime versions | +| ACP-backed agent | Initialize, new session, two turns, load, cancel, errors | +| Direct CLI agent | Version or help output and an interactive turn | +| Sandboxed agent | Policy matrix, runtime isolation probe, denied-operation state | +| Gateway-backed agent | Non-production gateway E2E and redacted OpenAB logs | +| Model compatibility | Target model selected and one live turn completed | +| Permission or sandbox | Every advertised mode, override behavior, runtime probe | +| Authentication | Isolated credential flow and restart persistence | +| Session persistence | Restart followed by session load and another turn | +| Gateway behavior | Non-production Discord E2E with logs | +| Deployment change | Health, rollout, one canary agent, rollback evidence | + +## Report Results + +Use one PR comment as the canonical report and link the Discord discussion when maintainers are coordinating there. + +```markdown +## Canary report + +### Test target + +- PR head: `` +- Image: `` +- Digest: `` +- Platform and runtime: `` +- Reproducer or client source: `` +- Gateway evidence: `` + +### Results + +| Check | Result | Evidence | +|------|--------|----------| +| Image inspection | Pass/Fail/Not verified | ... | +| ACP protocol smoke | Pass/Fail/Not verified | ... | +| Runtime isolation | Pass/Fail/Not verified | ... | +| Interactive validation | Pass/Fail/Not verified | ... | +| Discord gateway E2E | Pass/Fail/Not verified | ... | +| Rollback | Pass/Fail/Not verified | ... | + +### Conclusion + +State what the evidence proves, what it does not prove, remaining blockers, and whether the PR is ready for the next rollout gate. +``` + +Use `Pass` only when direct evidence exists. A published image, skipped job, or successful initialization is not evidence for multi-turn, cancellation, permission, persistence, gateway, or rollback behavior. + +## Clean Up + +After testing: + +- stop the canary deployment; +- remove temporary containers, workspaces, and credential volumes; +- revoke temporary credentials when applicable; +- confirm no canary instance still uses the bot identity; +- preserve only redacted evidence required by the PR; +- restore the baseline image if the canary replaced an existing deployment. + +For the local Docker example above: + +```bash +docker rm -f "$CANARY_NAME" 2>/dev/null || true +docker volume rm \ + openab-canary-auth \ + openab-canary-state \ + openab-canary-workspace +rm -f "$ENV_FILE" "$CONFIG_FILE" + +docker ps -a --filter "name=${CANARY_NAME}" +docker volume ls --filter name=openab-canary +``` + +An empty container and volume listing confirms local cleanup. Rotate or revoke the temporary bot token separately in the provider's control plane when the identity will not be reused. + +## Post-Merge Canary + +After merge, deploy the released image to one canary agent before broad rollout. Repeat the critical user flow against the merged artifact, monitor OpenAB logs for new initialization or turn errors, and verify session and credential persistence. Roll back to the recorded baseline tag if an acceptance criterion fails. + +The preview and post-merge canaries answer different questions. A successful preview proves the PR artifact; a successful post-merge canary proves that the released artifact works in the target deployment path. + +For a Kubernetes canary deployment, record the current image before changing it, wait for rollout health, and keep the exact rollback command ready: + +```bash +DEPLOYMENT=openab-codex +CONTAINER=openab +RELEASE_TAG=0.9.1 +RELEASE_IMAGE="ghcr.io/openabdev/openab:${RELEASE_TAG}-codex" +BASELINE_IMAGE="$(kubectl get deployment "$DEPLOYMENT" -o jsonpath="{.spec.template.spec.containers[?(@.name=='${CONTAINER}')].image}")" + +printf 'Baseline: %s\nCandidate: %s\n' "$BASELINE_IMAGE" "$RELEASE_IMAGE" +kubectl set image "deployment/${DEPLOYMENT}" "${CONTAINER}=${RELEASE_IMAGE}" +kubectl rollout status "deployment/${DEPLOYMENT}" --timeout=5m +kubectl logs "deployment/${DEPLOYMENT}" --since=10m +``` + +If an acceptance criterion fails, restore the recorded image rather than relying on memory: + +```bash +kubectl set image "deployment/${DEPLOYMENT}" "${CONTAINER}=${BASELINE_IMAGE}" +kubectl rollout status "deployment/${DEPLOYMENT}" --timeout=5m +``` + +## Worked Example + +The worked [Codex ACP migration canary report][pr-1353-canary] demonstrates five complementary validation layers: image inspection, ACP lifecycle testing, direct runtime probes, human-driven CLI verification, and a dedicated-bot Discord E2E with screenshot evidence. Its temporary ACP client was written in Node.js, but the transport and pass criteria above are language-independent. + +[pr-1353-canary]: https://github.com/openabdev/openab/pull/1353#issuecomment-4947408465 From 47d027592ee7c74cfc2538a19178251414088d50 Mon Sep 17 00:00:00 2001 From: chaodu-agent Date: Sat, 11 Jul 2026 19:11:32 -0400 Subject: [PATCH 2/2] =?UTF-8?q?docs:=20drop=20README=20architecture=20upda?= =?UTF-8?q?tes=20=E2=80=94=20moving=20to=20a=20separate=20PR?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Keep this PR scoped to the canary-testing process docs (guide + CONTRIBUTING E2E fix). The README architecture diagram rewrite is an independent change. --- README.md | 53 +++++++++++++++++++------------------------------ README.zh-TW.md | 53 +++++++++++++++++++------------------------------ 2 files changed, 40 insertions(+), 66 deletions(-) diff --git a/README.md b/README.md index 6c6341ec4..c927e9f2f 100644 --- a/README.md +++ b/README.md @@ -6,45 +6,32 @@ English | [繁體中文](README.zh-TW.md) ![OpenAB banner](images/banner.jpg) -A lightweight, secure, cloud-native ACP harness that bridges **Discord, Slack**, and any [Agent Client Protocol](https://github.com/anthropics/agent-protocol)-compatible coding CLI (Kiro CLI, Claude Code, Codex, Gemini, OpenCode, MiMo-Code, Copilot CLI, Hermes, Grok Build, Devin, Antigravity, Pi, etc.) over stdio JSON-RPC — delivering the next-generation development experience. **Telegram, LINE, Feishu/Lark, Google Chat, WeCom, and Microsoft Teams** are available through gateway adapters, either compiled into the unified binary or deployed as the standalone [Custom Gateway](crates/openab-gateway/). +A lightweight, secure, cloud-native ACP harness that bridges **Discord, Slack**, and any [Agent Client Protocol](https://github.com/anthropics/agent-protocol)-compatible coding CLI (Kiro CLI, Claude Code, Codex, Gemini, OpenCode, MiMo-Code, Copilot CLI, Hermes, Grok Build, Devin, Antigravity, Pi, etc.) over stdio JSON-RPC — delivering the next-generation development experience. **Telegram, LINE, Feishu/Lark, Google Chat**, and other webhook-based platforms are supported via the standalone [Custom Gateway](crates/openab-gateway/). 🪼 **Join our community!** Come say hi on Discord — we'd love to have you: **[🪼 OpenAB — Official](https://openab.dev/discord)** 🎉 ``` -┌──────────────┐ Gateway WS ┌─────────────────┐ ACP stdio ┌─────────────────────────┐ -│ Discord │◄────────────►│ │─────────────►│ agent runtime │ -│ User │ │ openab (Rust) │◄──JSON-RPC──│ (ACP process) │ -├──────────────┤ Socket Mode │ thin ACP broker │ ├─────────────────────────┤ -│ Slack │◄────────────►│ │ │ kiro-cli acp │ -│ User │ └────────┬────────┘ │ claude-agent-acp │ -├──────────────┤ ▲ │ codex-acp │ -│ Telegram │◄webhook/API─►┐ │ │ └─ Codex app-server │ -│ User │ │ │ WebSocket │ gemini --acp │ -├──────────────┤ │ │ (standalone) │ copilot --acp │ -│ LINE │◄webhook/API─►┤ │ or in-process │ cursor-agent acp │ -│ User │ │ │ (unified) │ hermes-acp │ -├──────────────┤ │ ┌──────┴───────────┐ │ opencode acp │ -│ Feishu/Lark │◄─WS/webhook─►┼►│ gateway adapters │ │ mimo acp │ -│ User │ │ │ standalone or │ │ grok agent stdio │ -├──────────────┤ │ │ embedded unified │ │ devin acp │ -│ Google Chat │◄webhook/API─►┤ └──────────────────┘ │ agy-acp │ -│ User │ │ │ pi-acp │ -├──────────────┤ │ │ openab-agent │ -│ WeCom │◄webhook/API─►┤ │ agentcore-acp │ -│ User │ │ │ └─ AWS AgentCore │ -├──────────────┤ │ │ custom ACP agent │ -│ MS Teams │◄webhook/API─►┘ └─────────────────────────┘ -│ User │ +┌──────────────┐ Gateway WS ┌──────────────┐ ACP stdio ┌──────────────────┐ +│ Discord │◄─────────────►│ │──────────────►│ coding CLI │ +│ User │ │ openab │◄── JSON-RPC ──│ (acp mode) │ +├──────────────┤ Socket Mode │ (Rust) │ ├──────────────────┤ +│ Slack │◄─────────────►│ │ │ kiro-cli acp │ +│ User │ └──────┬───────┘ │ claude-agent-acp │ +├──────────────┤ │ WebSocket │ codex-acp │ +│ Telegram │◄──webhook──┐ │ (outbound) │ gemini --acp │ +│ User │ │ │ │ copilot --acp │ +├──────────────┤ ▼ ▼ │ hermes-acp │ +│ LINE │◄──webhook──┌──────────────────┐ │ opencode acp │ +│ User │ │ Custom Gateway │ │ mimo acp │ +├──────────────┤ │ (standalone) │ │ grok agent stdio │ +│ Feishu/Lark │◄───WS──────│ │ │ devin acp │ +│ User │ │ │ │ agy-acp │ +├──────────────┤ │ │ │ pi-acp │ +│ Google Chat │◄──webhook──│ │ └──────────────────┘ +│ User │ └──────────────────┘ └──────────────┘ ``` -OpenAB remains a thin broker: platform adapters converge on the same -dispatcher and session pool, then cross one ACP stdio boundary to the selected -agent runtime. Gateway adapters can run as a standalone companion or be -embedded in a unified build without changing that boundary. Platform-edge -labels distinguish inbound delivery from outbound replies: `webhook/API` for -webhook platforms and `WS/webhook` for Feishu/Lark. - ## Demo ![openab demo](images/demo.png) @@ -52,7 +39,7 @@ webhook platforms and `WS/webhook` for Feishu/Lark. ## Features - **Multi-platform** — supports Discord and Slack, run one or both simultaneously -- **Gateway adapters** — extend to Telegram, LINE, Feishu/Lark, Google Chat, WeCom, and Microsoft Teams through the standalone [gateway](crates/openab-gateway/) or an opt-in unified build +- **Custom Gateway** — extend to Telegram, LINE, Feishu/Lark, Google Chat, MS Teams via standalone [gateway](crates/openab-gateway/) - **Pluggable agent backend** — swap between Kiro CLI, Claude Code, Codex, Gemini, OpenCode, MiMo-Code, Copilot CLI, Hermes, Grok Build, Devin, Antigravity, Pi via config - **@mention trigger** — mention the bot in an allowed channel to start a conversation - **Thread-based multi-turn** — auto-creates threads; no @mention needed for follow-ups diff --git a/README.zh-TW.md b/README.zh-TW.md index 36a49a71b..1db0ed09b 100644 --- a/README.zh-TW.md +++ b/README.zh-TW.md @@ -6,45 +6,32 @@ ![OpenAB banner](images/banner.jpg) -一個輕量、安全、雲端原生的 ACP harness,透過 stdio JSON-RPC 將 **Discord、Slack** 與任何相容於 [Agent Client Protocol](https://github.com/anthropics/agent-protocol) 的程式開發 CLI(Kiro CLI、Claude Code、Codex、Gemini、OpenCode、MiMo-Code、Copilot CLI、Hermes、Grok Build、Devin、Antigravity、Pi 等)連接起來,帶來新一代的開發體驗。**Telegram、LINE、Feishu/Lark、Google Chat、WeCom 與 Microsoft Teams** 則由 gateway adapters 支援;可將 adapters 編入 unified binary,或部署為獨立的 [Custom Gateway](crates/openab-gateway/)。 +一個輕量、安全、雲端原生的 ACP harness,透過 stdio JSON-RPC 將 **Discord、Slack** 與任何相容於 [Agent Client Protocol](https://github.com/anthropics/agent-protocol) 的程式開發 CLI(Kiro CLI、Claude Code、Codex、Gemini、OpenCode、MiMo-Code、Copilot CLI、Hermes、Grok Build、Devin、Antigravity、Pi 等)連接起來,帶來新一代的開發體驗。**Telegram、LINE、Feishu/Lark、Google Chat** 以及其他以 webhook 為基礎的平台,則透過獨立的 [Custom Gateway](crates/openab-gateway/) 支援。 🪼 **加入我們的社群!** 歡迎到 Discord 和大家打招呼:**[🪼 OpenAB — Official](https://openab.dev/discord)** 🎉 ``` -┌──────────────┐ Gateway WS ┌─────────────────┐ ACP stdio ┌─────────────────────────┐ -│ Discord │◄────────────►│ │─────────────►│ agent runtime │ -│ User │ │ openab (Rust) │◄──JSON-RPC──│ (ACP process) │ -├──────────────┤ Socket Mode │ thin ACP broker │ ├─────────────────────────┤ -│ Slack │◄────────────►│ │ │ kiro-cli acp │ -│ User │ └────────┬────────┘ │ claude-agent-acp │ -├──────────────┤ ▲ │ codex-acp │ -│ Telegram │◄webhook/API─►┐ │ │ └─ Codex app-server │ -│ User │ │ │ WebSocket │ gemini --acp │ -├──────────────┤ │ │ (standalone) │ copilot --acp │ -│ LINE │◄webhook/API─►┤ │ or in-process │ cursor-agent acp │ -│ User │ │ │ (unified) │ hermes-acp │ -├──────────────┤ │ ┌──────┴───────────┐ │ opencode acp │ -│ Feishu/Lark │◄─WS/webhook─►┼►│ gateway adapters │ │ mimo acp │ -│ User │ │ │ standalone or │ │ grok agent stdio │ -├──────────────┤ │ │ embedded unified │ │ devin acp │ -│ Google Chat │◄webhook/API─►┤ └──────────────────┘ │ agy-acp │ -│ User │ │ │ pi-acp │ -├──────────────┤ │ │ openab-agent │ -│ WeCom │◄webhook/API─►┤ │ agentcore-acp │ -│ User │ │ │ └─ AWS AgentCore │ -├──────────────┤ │ │ custom ACP agent │ -│ MS Teams │◄webhook/API─►┘ └─────────────────────────┘ -│ User │ +┌──────────────┐ Gateway WS ┌──────────────┐ ACP stdio ┌──────────────────┐ +│ Discord │◄─────────────►│ │──────────────►│ coding CLI │ +│ User │ │ openab │◄── JSON-RPC ──│ (acp mode) │ +├──────────────┤ Socket Mode │ (Rust) │ ├──────────────────┤ +│ Slack │◄─────────────►│ │ │ kiro-cli acp │ +│ User │ └──────┬───────┘ │ claude-agent-acp │ +├──────────────┤ │ WebSocket │ codex-acp │ +│ Telegram │◄──webhook──┐ │ (outbound) │ gemini --acp │ +│ User │ │ │ │ copilot --acp │ +├──────────────┤ ▼ ▼ │ hermes-acp │ +│ LINE │◄──webhook──┌──────────────────┐ │ opencode acp │ +│ User │ │ Custom Gateway │ │ mimo acp │ +├──────────────┤ │ (standalone) │ │ grok agent stdio │ +│ Feishu/Lark │◄───WS──────│ │ │ devin acp │ +│ User │ │ │ │ agy-acp │ +├──────────────┤ │ │ │ pi-acp │ +│ Google Chat │◄──webhook──│ │ └──────────────────┘ +│ User │ └──────────────────┘ └──────────────┘ ``` -OpenAB 仍是 thin broker:所有 platform adapters 都會進入同一套 -dispatcher 與 session pool,再透過單一 ACP stdio boundary 連接所選的 -agent runtime。Gateway adapters 可作為 standalone companion 執行,也可 -內嵌於 unified build,兩種部署方式都不會改變這個 boundary。Platform -edge 的 labels 會分別呈現 inbound delivery 與 outbound reply:webhook -platforms 使用 `webhook/API`,Feishu/Lark 則使用 `WS/webhook`。 - ## 示範 ![openab demo](images/demo.png) @@ -52,7 +39,7 @@ platforms 使用 `webhook/API`,Feishu/Lark 則使用 `WS/webhook`。 ## 功能特色 - **多平台支援** — 支援 Discord 與 Slack,可單獨或同時執行 -- **Gateway adapters** — 可透過獨立的 [gateway](crates/openab-gateway/) 或 opt-in unified build 擴充至 Telegram、LINE、Feishu/Lark、Google Chat、WeCom 與 Microsoft Teams +- **Custom Gateway** — 透過獨立的 [gateway](crates/openab-gateway/) 擴充至 Telegram、LINE、Feishu/Lark、Google Chat、MS Teams - **可替換的 agent backend** — 可透過設定在 Kiro CLI、Claude Code、Codex、Gemini、OpenCode、MiMo-Code、Copilot CLI、Hermes、Grok Build、Devin、Antigravity、Pi 之間切換 - **@mention 觸發** — 在允許的頻道中 mention bot,即可開始對話 - **以討論串進行多輪對話** — 自動建立討論串;後續訊息不需再次 @mention