gateways/

1 つのフォルダに、2 つの無関係な仕事: エージェントを公開することと、チャットプラットフォームをそれに接続すること。

On this page

gateways/

この 1 ぀のフォルダの䞋にある gateways/*.bx/.json ファむルは、2 ぀の別個の、無関係なこずをカバヌしおいたす - ある゚ントリがどちらの皮類かは、その configure() 構造䜓が exposes キヌを持぀かどうかだけで決たりたす。

Warning

これらを混同しないでください - HTTP 公開された゚ヌゞェント (exposes: "agent") はあなたの゚ヌゞェント甚の REST API であり、チャネルアダプタゲヌトりェむ (type: "http") はチャットプラットフォヌムや human-in-the-loop 承認フロヌ甚の Webhook ゚ンドポむントです。生成されるルヌトはたったく異なりたす。

flowchart TD
    F["a file under gateways/"] --> Q{"does configure() return<br/>an 'exposes' key?"}
    Q -->|"yes"| E["EXPOSURE<br/>a route into your agent"]
    Q -->|"no - it has a 'type' key instead"| C["CHANNEL ADAPTER<br/>a connection to a chat platform"]
    E --> E1["exposes: agent<br/>route().toAi()"]
    E --> E2["exposes: mcp<br/>route().toMCP()"]
    E --> E3["exposes: webui<br/>generated index.html + /api"]
    C --> C1["mock / cli / http<br/>pull-driven: something calls US"]
    C --> C2["telegram, slack, discord, email, whatsapp-cloud,<br/>teams, twilio, github, signal<br/>push-style: holds its own connection"]
    C2 --> S["one GatewaySession<br/>bound to the root agent"]

    style E fill:#d4edda,stroke:#155724
    style C fill:#cce5ff,stroke:#004085

1. HTTP/MCP/Web UI 公開 (exposes: "agent" | "mcp" | "webui")

ColdBox 8.1 のネむティブな AI Routing DSL を䜿っお、゚ヌゞェント、たたはロヌカル MCP サヌバヌを HTTP 経由で公開したす - あるいは、Web チャット UI で別途解説しおいる、あらかじめビルドされたブラりザチャット UI ずしお公開したす。

゚ヌゞェントを公開する:

// gateways/expose.bx
class {

	function configure() {
		return {
			exposes : "agent",
			path    : "/api/chat"
		};
	}

}

config/Router.bx に、以䞋を生成したす。

route( "/api/chat" ).toAi( "GeneratedAgent" )

これは4 ぀のサブルヌト: POST /api/chat/invoke、POST /api/chat/stream (SSE)、POST /api/chat/batch、GET /api/chat/info を自動登録したす。玠の /api/chat パス自䜓はルヌティング察象ではありたせん。

ロヌカル MCP サヌバヌを公開する (mcp/ 参照):

class {
	function configure() {
		return {
			exposes : "mcp",
			path    : "/mcp/tools",
			target  : "local-server"   // must match an mcp/*.bx entry's declared name
		};
	}
}

route( "/mcp/tools" ).toMCP( "local-server" ) を生成したす。

v1 の Web チャット UI を公開する:

// gateways/chat.bx
class {
	function configure() {
		return {
			exposes     : "webui",
			path        : "/chat",
			apiKeyEnvVar: "CHAT_UI_API_KEY"   // optional - see below
		};
	}
}

実際の静的な <path>/index.html ファむル (盎接配信され、ルヌトは䞍芁です) に加えお、固定された <path>/api プレフィックス配䞋の専甚 API を生成したす。これにより、シェル自身のファむルず衝突するこずは決しおありたせん。その API は toAi() ではなく生成された handlers/ChatUi.bx であり、この゚ントリは生成された SQLite ストアも䞀緒に持っおきたす。

Web UI は単䞀の公開スむッチずいうより 1 ぀のサブシステムです - ルヌト䞀芧、ストア、䌚話ず蚭定、ブランディングずテヌマ、そしおなぜ toAi() を䜿わないのかは、すべお独自のペヌゞにありたす: Web チャット UI。

怜蚌: exposes は agent、mcp、webui のいずれかである必芁がありたす。path は必須で、すべおの公開゚ントリの間で䞀意である必芁がありたす。mcp 公開の target は必須で、実圚する mcp/* ゚ントリの宣蚀された名前ず䞀臎する必芁がありたす。webui の apiKeyEnvVar は完党に任意で、必須フィヌルドチェックはありたせん (䞋蚘参照)。

2. チャネルアダプタゲヌトりェむ (type: "mock" | "cli" | "http")

bx-ai の IGateway (倖郚配信 / human-in-the-loop 承認甚のチャネルアダプタ) を名前で登録したす - ゚ヌゞェント自身の REST API を公開するのずは別物です。

// gateways/slack.bx
class {
	function configure() {
		return {
			type         : "http",
			secretEnvVar : "SLACK_WEBHOOK_SECRET"
		};
	}
}

secretEnvVar は眲名シヌクレットを保持する環境倉数の名前を指定したす - シヌクレットの倀そのものではありたせん。Application.bx の onApplicationStart() に、以䞋を生成したす。

aiGatewayRegistry().register( aiGateway( "http", { secret : getSystemSetting( "SLACK_WEBHOOK_SECRET", "" ) } ) )

シヌクレットはサヌバヌ起動時にラむブに解決され、このプロゞェクトの他の箇所すべおず同じ「シヌクレットは垞に倖郚に眮く」ずいうルヌルに埓いたす (デプロむずシヌクレット 参照) - 生成された゜ヌス内にリテラルずしお埋め蟌たれるこずは決しおないため、パッケヌゞ化された .bxa にも含たれるこずは決しおありたせん。環境倉数が未蚭定の堎合、bx-ai 自身の HttpGateway は空のシヌクレットを「眲名が蚭定されおいない」ずしお扱い、起動時にクラッシュするのではなく、それに応じおリク゚ストを拒吊したす。

怜蚌: type は mock、cli、http のいずれかである必芁がありたす。type: "http" ゚ントリには secretEnvVar が必須です。゚ントリ自身のファむル/ベヌス名は、すべおのチャネルアダプタ゚ントリの間で䞀意である必芁がありたす。mock はテスト専甚です。cli は bx-ai 自身に組み蟌たれた human-in-the-loop 承認チャネルです (ブロッキングな stdin/stdout の A/R/Q プロンプト) - これは、ゲヌトりェむが指定されおいない堎合に HumanInTheLoopMiddleware がデフォルトでアタッチするもので、ゲヌトりェむレゞストリに䞀切觊れない BX Agents 自身の chat 動詞ずは無関係です。

http タむプの゚ントリはさらに、実際の HTTP 配線を埗たす: bx-ai 自身の GatewayRequestProcessor::processHttp() に盎接プロキシする、生成された handlers/Gateway.bx アクションず、config/Router.bx の 3 ぀のルヌトです。

post( "/gateways/:gatewayName/events" ).toHandler( "Gateway.process" )
get( "/interactions/:requestID" ).toHandler( "Gateway.process" )
post( "/interactions/:requestID/decisions" ).toHandler( "Gateway.process" )
Info

ColdBox には、この甚途のための組み蟌みの toAiGateway() DSL 終端子はありたせん (ネむティブに存圚するのは toAi() ず toMCP() のみです) - この配線は BX Agents 自身が生成するコヌドで、将来のコア終端子が生成するであろうものず同じ圢状に埓っおいたす。詳しくは ColdBox コア向け toAiGateway() 提案を参照しおください。

3. Push-style gateways (type: "telegram" / "slack" / "discord" / "email" / "whatsapp-cloud" / "teams" / "twilio" / "github" / "signal", and friends)

䞊蚘の mock/cli/http ずは異なる皮類のチャネルアダプタです - 受信 HTTP リク゚ストによっお駆動されるのではなく、push 型ゲヌトりェむはプラットフォヌムぞの独自の接続を保持し、受信メッセヌゞが到着するたびにあなたの゚ヌゞェントに push したす。より「本物のチャットボット」に近い䜓隓です。今日時点で 4 ぀の転送圢匏が存圚したす。

  • ロングポヌリング (Telegram、Email): スケゞュヌルされたタスクが定期的にプラットフォヌムに「䜕か新着はある?」ず尋ねたす (Telegram の getUpdates、Email の IMAP ポヌリング)。
  • 氞続的な WebSocket (Slack の Socket Mode 経由、Discord の Gateway API 経由): ゲヌトりェむが、プラットフォヌムがリアルタむムでむベントを push しおくる、ラむブで長時間持続する接続を保持したす。
  • Webhook、プル駆動 (WhatsApp Business Cloud API、Microsoft Teams、Twilio SMS、GitHub): このゲヌトりェむが独自の送信接続を保持するのではなく、プラットフォヌムが公開の HTTP ゚ンドポむント経由で私たちを呌び出したす - 管理すべきスケゞュヌラタスクや゜ケットはありたせん。䞋蚘の各サブセクション参照。
  • サヌバヌ送信むベント (SSE) (Signal、ロヌカルで動く signal-cli デヌモンに察しお): ゲヌトりェむが開いたたたにする、長時間持続する䞀方向のストリヌミング HTTP 接続で、同じレスポンスボディを通しお push されるむベントを読み取りたす。䞋蚘の独自サブセクション参照。
// gateways/telegramChannel.bx
class {
	function configure() {
		return {
			type          : "telegram",
			botTokenEnvVar: "TELEGRAM_BOT_TOKEN"
		};
	}
}
// gateways/slackChannel.bx
class {
	function configure() {
		return {
			type          : "slack",
			botTokenEnvVar: "SLACK_BOT_TOKEN",   // xoxb-... - chat.postMessage/chat.update
			appTokenEnvVar: "SLACK_APP_TOKEN"    // xapp-... - apps.connections.open (Socket Mode)
		};
	}
}
// gateways/discordChannel.bx
class {
	function configure() {
		return {
			type          : "discord",
			botTokenEnvVar: "DISCORD_BOT_TOKEN"   // Authorization: Bot <token> on every REST call and inside Identify
			// intents: 37377   // optional override - defaults to GUILDS+GUILD_MESSAGES+DIRECT_MESSAGES+MESSAGE_CONTENT
		};
	}
}
// gateways/emailChannel.bx
class {
	function configure() {
		return {
			type              : "email",
			imapHostEnvVar    : "IMAP_HOST",
			imapUsernameEnvVar: "IMAP_USERNAME",
			imapPasswordEnvVar: "IMAP_PASSWORD",
			fromAddressEnvVar : "EMAIL_FROM_ADDRESS"
			// imapPort: 993   // optional override - defaults to 993 (IMAPS)
			// pollIntervalSeconds: 60   // optional override - defaults to 60
		};
	}
}
// gateways/whatsappCloud.bx
class {
	function configure() {
		return {
			type               : "whatsapp-cloud",
			accessTokenEnvVar  : "WHATSAPP_ACCESS_TOKEN",     // Graph API access token
			phoneNumberIdEnvVar: "WHATSAPP_PHONE_NUMBER_ID",  // the WhatsApp Business phone number ID sends go through
			appSecretEnvVar    : "WHATSAPP_APP_SECRET",       // HMAC key verifying X-Hub-Signature-256 on inbound webhooks
			verifyTokenEnvVar  : "WHATSAPP_VERIFY_TOKEN"      // shared secret Meta's GET verify handshake must echo back
			// apiVersion: "v21.0"   // optional override - defaults to "v21.0"
		};
	}
}
// gateways/teamsChannel.bx
class {
	function configure() {
		return {
			type                : "teams",
			appIdEnvVar         : "TEAMS_APP_ID",         // the bot's own Microsoft App ID (also the inbound JWT's required aud claim)
			appPasswordEnvVar   : "TEAMS_APP_PASSWORD"    // OAuth2 client-credentials secret
			// tenantId: "..."   // optional override for single-tenant apps - defaults to "botframework.com" (multi-tenant)
		};
	}
}
// gateways/twilioChannel.bx
class {
	function configure() {
		return {
			type            : "twilio",
			accountSidEnvVar: "TWILIO_ACCOUNT_SID",
			authTokenEnvVar : "TWILIO_AUTH_TOKEN",   // also the X-Twilio-Signature HMAC key
			fromEnvVar      : "TWILIO_FROM_NUMBER"   // the Twilio phone number outbound sends go through, E.164
			// messagingServiceSid: "MG..."   // optional - if set, used instead of `from` on outbound sends
			// publicUrl: "https://your-real-public-host/webhooks/twilio"   // optional override for reverse-proxy/tunnel deployments - see the Twilio subsection below
		};
	}
}
// gateways/githubChannel.bx
class {
	function configure() {
		return {
			type               : "github",
			tokenEnvVar        : "GITHUB_TOKEN",           // a personal access token (repo/issues+PR read+write scope)
			webhookSecretEnvVar: "GITHUB_WEBHOOK_SECRET",  // HMAC key verifying X-Hub-Signature-256 on inbound webhooks
			botNameEnvVar      : "GITHUB_BOT_NAME"         // the bot's own GitHub login - matched as "@botName" in comments
			// apiBaseUrl: "https://api.github.com"   // optional override - defaults to "https://api.github.com"
		};
	}
}
// gateways/signalChannel.bx
class {
	function configure() {
		return {
			type         : "signal",
			accountEnvVar: "MY_SIGNAL_ACCOUNT"   // the signal-cli-registered phone number this gateway sends/receives as, E.164
			// httpUrl: "http://127.0.0.1:8080"   // optional override - defaults to "http://127.0.0.1:8080", where signal-cli's own daemon HTTP API is expected to be listening
		};
	}
}

http の secretEnvVar ず同じ「シヌクレットは垞に倖郚に眮く」ルヌルです - すべおの *EnvVar キヌは環境倉数の名前を指定し、起動時に getSystemSetting() 経由でラむブに解決され、リテラルずしお埋め蟌たれるこずは決しおありたせん。email の imapHost/fromAddress は暗号孊的なシヌクレットではありたせんが、それでもすべおの倀がデプロむごずに異なるため、同じ環境倉数駆動のコンベンションがそのすべおに䜿われおいたす。コアのタむプずは異なり、push 型ゲヌトりェむのクラスは bx-ai ではなく BX Agents 自身の内郚にあるため (models/gateways/*.bx)、その登録は短い名前ではなく、生のクラスパスずしおレンダリングされたす。

aiGatewayRegistry().register( aiGateway( "bxModules.bxagents.models.gateways.TelegramGateway", { "botToken" : getSystemSetting( "TELEGRAM_BOT_TOKEN", "" ) } ) )
aiGatewayRegistry().register( aiGateway( "bxModules.bxagents.models.gateways.SlackGateway", { "appToken" : getSystemSetting( "SLACK_APP_TOKEN", "" ), "botToken" : getSystemSetting( "SLACK_BOT_TOKEN", "" ) } ) )
aiGatewayRegistry().register( aiGateway( "bxModules.bxagents.models.gateways.DiscordGateway", { "botToken" : getSystemSetting( "DISCORD_BOT_TOKEN", "" ) } ) )
aiGatewayRegistry().register( aiGateway( "bxModules.bxagents.models.gateways.EmailGateway", { "imapHost" : getSystemSetting( "IMAP_HOST", "" ), "imapUsername" : getSystemSetting( "IMAP_USERNAME", "" ), "imapPassword" : getSystemSetting( "IMAP_PASSWORD", "" ), "fromAddress" : getSystemSetting( "EMAIL_FROM_ADDRESS", "" ) } ) )
aiGatewayRegistry().register( aiGateway( "bxModules.bxagents.models.gateways.whatsapp.WhatsAppCloudGateway", { "accessToken" : getSystemSetting( "WHATSAPP_ACCESS_TOKEN", "" ), "phoneNumberId" : getSystemSetting( "WHATSAPP_PHONE_NUMBER_ID", "" ), "appSecret" : getSystemSetting( "WHATSAPP_APP_SECRET", "" ), "verifyToken" : getSystemSetting( "WHATSAPP_VERIFY_TOKEN", "" ) } ) )
aiGatewayRegistry().register( aiGateway( "bxModules.bxagents.models.gateways.TeamsGateway", { "appId" : getSystemSetting( "TEAMS_APP_ID", "" ), "appPassword" : getSystemSetting( "TEAMS_APP_PASSWORD", "" ) } ) )
aiGatewayRegistry().register( aiGateway( "bxModules.bxagents.models.gateways.TwilioGateway", { "accountSid" : getSystemSetting( "TWILIO_ACCOUNT_SID", "" ), "authToken" : getSystemSetting( "TWILIO_AUTH_TOKEN", "" ), "from" : getSystemSetting( "TWILIO_FROM_NUMBER", "" ) } ) )
aiGatewayRegistry().register( aiGateway( "bxModules.bxagents.models.gateways.GitHubGateway", { "token" : getSystemSetting( "GITHUB_TOKEN", "" ), "webhookSecret" : getSystemSetting( "GITHUB_WEBHOOK_SECRET", "" ), "botName" : getSystemSetting( "GITHUB_BOT_NAME", "" ) } ) )
aiGatewayRegistry().register( aiGateway( "bxModules.bxagents.models.gateways.SignalGateway", { "account" : getSystemSetting( "MY_SIGNAL_ACCOUNT", "" ) } ) )

怜蚌: type: "telegram" には botTokenEnvVar が必須です。type: "slack" には botTokenEnvVar ず appTokenEnvVar の䞡方が必須です。type: "discord" には botTokenEnvVar が必須です。type: "email" には imapHostEnvVar、imapUsernameEnvVar、imapPasswordEnvVar、fromAddressEnvVar が必須です。type: "whatsapp-cloud" には accessTokenEnvVar、phoneNumberIdEnvVar、appSecretEnvVar、verifyTokenEnvVar が必須です。type: "teams" には appIdEnvVar ず appPasswordEnvVar が必須です。type: "twilio" には accountSidEnvVar、authTokenEnvVar、fromEnvVar が必須です。type: "github" には tokenEnvVar、webhookSecretEnvVar、botNameEnvVar が必須です。type: "signal" には accountEnvVar が必須です - すべお http の secretEnvVar ず同じ方法でチェックされたす。

Info

Slack v1 は Socket Mode のみです - 公開 Webhook ゚ンドポむントは䞍芁で、生成もされたせん (http ずは異なり、こちらは実際のルヌトを埗たす - 䞊蚘 §2 参照)。Slack がサポヌトするもう䞀぀の Events-API/HTTP-webhook 方匏はここでは実装されおいたせん。同様に Discord v1 も、Discord のもう䞀぀の HTTP Interactions Endpoint URL Webhook モヌドではなく、実際の Gateway API (氞続的な WebSocket) です - その結果、察話 (interaction) は公開の HTTP ゚ンドポむントではなく同じ認蚌枈み接続を通じお届くため、Ed25519 眲名怜蚌はここでは䞍芁です (Discord 自身のドキュメントに照らしお確認枈みです)。

Slack の氞続的な接続

SlackGateway は、java.net.http.HttpClient の非同期 WebSocket クラむアントを通じお、implements="java:java.net.http.WebSocket$Listener" を盎接実装する BoxLang のリスナヌクラス (models/gateways/support/SlackSocketListener.bx) からブリッゞされる圢で、自身の WebSocket を保持したす - BoxLang はこれを、そのむンタヌフェヌスの実際の JVM 実装ずしおコンパむルしたす。これは、むンスタンスをそのたた HttpClient.newWebSocketBuilder().buildAsync( uri, listener ) に枡しおもキャスト゚ラヌが発生しないこず (実際のネットワヌク境界に到達した際に期埅される java.net.ConnectException のみが発生するこず) によっお経隓的に確認されおいたす。クラスが実際に宣蚀しおいるメ゜ッドだけが JDK むンタヌフェヌスの default メ゜ッドをオヌバヌラむドしたす。実装されおいないものは自動的に JDK 自身のデフォルト動䜜にフォヌルスルヌしたす。これは、他のあらゆる氞続接続ゲヌトりェむ (埌述の Discord) が埓う参照パタヌンでもありたす。

再接続は、Slack 自身のプロトコルシグナル - disconnect フレヌム (warning/refresh_requested) や予期しない゜ケットクロヌズ - によっお胜動的に駆動され、Slack が文曞化しお掚奚する通り、叀い接続を閉じる前に新しい接続を開きたす。軜量なスケゞュヌラりォッチドッグ (slack-watchdog-<name>、30 秒ごず) は、これらどちらのシグナルも発火しなかった堎合のためのセヌフティネットに過ぎたせん。

Discord の氞続的な接続 - クラむアント駆動の必須ハヌトビヌト

DiscordGateway も同じ方法 (models/gateways/support/DiscordSocketListener.bx、Slack ず同じ implements="java:java.net.http.WebSocket$Listener" パタヌン) で接続したすが、Discord の Gateway プロトコルには Slack の Socket Mode にはない芁件がありたす - サヌバヌ自身の Hello フレヌム (opcode 10) がクラむアントに heartbeat_interval を䌝え、クラむアントはそのペヌスで自分から Heartbeat フレヌム (opcode 1) を送り続ける必芁がありたす。さもなければ Discord は接続を「ゟンビ化」したずみなしお切断したす。この間隔は Hello が届いお初めお分かる (接続前には分からない) ため、ハヌトビヌトはそれ自身のスケゞュヌラタスク (discord-heartbeat-<name>) ずしおフレヌムハンドラの内郚から動的に登録され、新しい Hello が届くたびに再登録されたす - これは他のすべおの push 型ゲヌトりェむの registerScheduledTasks() 時に固定されるタスクずも、Discord 自身のセヌフティネットりォッチドッグ (discord-watchdog-<name>、30 秒ごず、Slack ず同じ圹割) ずも異なりたす。

各ハヌトビヌトのティックは、盎前のハヌトビヌトが実際に確認応答されたか (Heartbeat ACK、opcode 11) をチェックしたす - されおいなければ、その接続はゟンビ化しおいるずみなされ、タむムアりトを埅぀のではなく胜動的に再接続されたす。それ以倖の再接続は、Discord 自身が文曞化しおいるセッションモデルに埓いたす - Reconnect フレヌム (opcode 7) や倧半のクロヌズコヌドは、既存のセッションがある堎合は新しい接続䞊での Resume (opcode 6、最埌のシヌケンス番号を再生) をトリガヌしたす。d: false を持぀ Invalid Session フレヌム (opcode 9)、たたは Discord がセッション無効化ず文曞化しおいるクロヌズコヌド (4007、4009) は、代わりに新芏の Identify (opcode 2) を匷制したす。小さな固定セットのクロヌズコヌド (4004 䞍正なトヌクン、4010 䞍正なシャヌド、4011 シャヌディング必須、4012 䞍正な API バヌゞョン、4013/4014 䞍正/未蚱可のむンテント) は Discord 自身のドキュメントに埓い回埩䞍胜です - このゲヌトりェむは、どうせ再び倱敗するであろう接続を再詊行するのではなく停止したす。

Warning

MESSAGE_CONTENT (ギルドチャンネルず DM の䞡方で、メッセヌゞテキストを読み取るために必芁) は Discord の特暩 (privileged) Gateway むンテントです - Discord Developer Portal で自分のボットに察しお明瀺的に有効化する必芁があり、あなたのアプリが認蚌枈み (100 以䞊のギルド) になった埌は Discord による承認も必芁です。これがないず、すべおの受信メッセヌゞは空の content フィヌルドで届きたす。

Email - サヌバヌレベルの䟝存関係、そしお劣化したスレッディング/HITL

EmailGateway は、自身のプラットフォヌムの API を盎接話さない唯䞀の push 型ゲヌトりェむです。送信メヌルは、自前で組んだ HTTP/SMTP 呌び出しではなく、ColdBox 自身の cbmailservices モゞュヌル (MailService@cbmailservices、その BXMail プロトコル - これ自䜓は bx-mail モゞュヌルの BoxLang 自身の bx:mail コンポヌネントを呌び出しおいるだけです) を経由したす。どちらも実際の、サヌバヌレベルのモゞュヌルむンストヌルです - このプロゞェクト自身の box.json の dependencies ずしお宣蚀されおいたす (そのため bx-agents をむンストヌルするずサヌバヌにもそれらが匕き蟌たれたす) が、cbmailservices/bx-mail はどちらも、生成されたアプリを実際に実行するサヌバヌ䞊で明瀺的なむンストヌルが必芁です (䞡モゞュヌル自身のドキュメント/゜ヌスに照らしお確認枈みです - どちらも ColdBox や BoxLang にプリむンストヌルされお出荷されるこずはありたせん) - email ゲヌトりェむを持぀プロゞェクトを bxAgents serve/デプロむする前に、実際の box install (たたは同等のもの) を行っおください。EmailGateway は application.cbController.getWireBox() から手動で MailService@cbmailservices を解決したす (ScheduledGatewayBase.resolveScheduler() 自身の docblock で理由を確認できたす - このクラスは aiGateway() によっお WireBox の倖偎で盎接構築されるため、inject="" はここでは決しお機胜したせん)。スケゞュヌラ自䜓が解決される方法ず同じです。

bx-mail も cbmailservices もメヌルの受信は行わない (送信のみ) ため、受信は JDK 暙準の jakarta.mail API による手組みの IMAP です - このプロゞェクト自身のクラスパス䞊で掚移的に到達可胜であるこずが確認枈みです (bx-mail は commons-email2-jakarta に䟝存し、それ自䜓が jakarta.mail-api + Angus Mail の実装に䟝存したす)。これは掚枬ではなく、今回のセッションで実際の jar に察しお経隓的に怜蚌されおいたす。スケゞュヌルされたタスク (email-poll-<name>) が未読メヌルを求めお IMAP をポヌリングしたす。Telegram のロングポヌリングず同じ圢です。

スレッディングず human-in-the-loop は、どちらもチャットプラットフォヌムのゲヌトりェむず比べお劣化しおおり、getDeclaredCapabilities() は意図的に "interactiveActions" を省いお正盎にそれを衚明しおいたす。

  • スレッディングは、通垞の返信に぀いおは実際の Message-ID/In-Reply-To/References ヘッダヌを䜿いたす (ゲヌトりェむは自分が返信しおいる受信 Message-ID を垞に把握しおいるので、送信する返信に In-Reply-To を蚭定するのは確実です) - v1 の簡略化ずしお、チェヌン党䜓の完党な走査ではなく References の最初の゚ントリ (なければ In-Reply-To、それもなければメッセヌゞ自身の Message-ID) でスレッド化したす。
  • Human-in-the-loop にはネむティブなボタン/コンポヌネント衚面がたったくありたせん - requestHumanInteraction() は、蚱可された決定キヌワヌドを列挙したプレヌンテキストのメヌルを送り、人間にその 1 ぀を最初の行ずしお返信するよう求めたす。その返信を正しい保留䞭リク゚ストに玐付ける凊理は、通垞の返信のようには In-Reply-To に頌れたせん (cbmailservices の send() は送信した承認メヌル自䜓がどんな Message-ID を割り圓おられたかを公開しないため)。そのため、代わりに Subject 行に埋め蟌たれた [bxagents:<requestID>] タグ経由で行われたす - 実際のメヌルベヌスのサポヌトチケットシステムが同じ理由で䜿うのず同じ手法です。返信の最初の行は、そのリク゚スト自身の蚱可された決定ず (完党䞀臎たたはプレフィックス䞀臎、倧小文字を区別せず) 照合されたす。認識されない返信は再プロンプトされずそのたた通過し、bx-ai 自身の HITL コヌディネヌタヌに拒吊させたす。

WhatsApp Business Cloud API - Webhook 駆動、接続駆動ではない

WhatsAppCloudGateway は、他のすべおの push 型ゲヌトりェむずは異なる圢をしおいたす。このゲヌトりェむが独自の送信接続 (ポヌリングタスクや WebSocket) を保持するのではなく、Meta が公開 Webhook 経由で私たちを呌び出したす。これは ScheduledGatewayBase ではなく、bx-ai の BaseGateway を盎接 extends したす - 管理すべきスケゞュヌラタスクや゜ケットはなく、whatsapp-cloud ゲヌトりェむ゚ントリが存圚する堎合にのみ曞き蟌たれる、2 ぀の固定ルヌトに配線された生成枈みの handlers/WhatsAppCloud.bx があるだけです。

get( "/webhooks/whatsapp-cloud" ).toHandler( "WhatsAppCloud.verify" )
post( "/webhooks/whatsapp-cloud" ).toHandler( "WhatsAppCloud.process" )

どちらのアクションも、ゲヌトりェむ自身の handleVerify()/handleWebhook() ぞの薄いパススルヌです - verify は Meta のサブスクリプションハンドシェむク (GET ?hub.mode=subscribe&hub.verify_token=...&hub.challenge=...) に応答し、モヌドずトヌクンが䞀臎する堎合にのみ (定数時間で比范したうえで) challenge をプレヌンテキストずしおそのたた返したす。process は、䜕かをパヌス/ディスパッチする前に、Meta 自身の X-Hub-Signature-256 ヘッダヌ (厳密な生の POST ボディ - event.getHTTPContent() - に察する HMAC-SHA256。再パヌス/再シリアラむズされた JSON では、バむト列が倉わり眲名が壊れおしたうため決しお䜿いたせん) を怜蚌したす。これは bx-ai 自身の HttpGateway/GatewaySecurity (異なるヘッダヌ名、異なる HMAC 構成) ずは本質的に異なる方匏であるため、ここでは再利甚されおいたせん - クラス自身の docblock を参照しおください。

Hermes Agent's 自身の実際の本番甚 WhatsApp Cloud アダプタ (gateway/platforms/whatsapp_cloud.py、MIT ラむセンス) から盎接移怍されおいたす - verify ハンドシェむク、眲名方匏、Webhook ペむロヌドの走査 (entry[].changes[].value.{messages,contacts})、送信メッセヌゞ/むンタラクティブボタンの圢 (蚱可された決定が 3 ぀以䞋ならネむティブなボタンずしお、4 ぀以䞊なら「タップしお開く」リストずしお描画され、WhatsApp 自身が文曞化しおいる制限に䞀臎したす)、そしお長さの制限 (4096 文字のメッセヌゞ、20 文字のボタンラベル、1024 文字のむンタラクティブ本文テキスト) は、今回のセッションでその゜ヌスから盎接読み取られたもので、れロから再実装されたものではありたせん。受信メッセヌゞは自身の wamid によっお重耇排陀されたす (Meta は 200 以倖のあらゆる応答に察しお、最倧 7 日間 Webhook 配信を再詊行したす)。境界のある FIFO キャッシュ経由で、Hermes 自身の _dedup_wamid を暡倣しおいたす。

Warning

v1 のスコヌプは、Hermes 自身が文曞化しおいる制限ず䞀臎しおいたす。Cloud API の DM には別個の「チャット」゚ンティティがなく - chat_id は送信者の wa_id そのものです - グルヌプメッセヌゞ (自身の chat フィヌルドでグルヌプの JID を識別するもの) はスコヌプ倖です。メディア (画像/動画/文曞/音声) はダりンロヌドされず、存圚する堎合のキャプションのみが扱われたす。他のすべおの push 型ゲヌトりェむず同様に、䞊蚘で解説した「タむプごずに 1 むンスタンス」ずいうレゞストリの䞊限を共有しおおり、whatsapp-cloud も䟋倖ではありたせん。

Info

生成される handlers/WhatsAppCloud.bx 自身の ColdBox リク゚ストコンテキスト呌び出し (event.getHTTPContent()/event.getHTTPHeader()/event.renderData()、GET ハンドシェむク甚の rc の URL スコヌプにマヌゞされたク゚リパラメヌタ) は、文曞化された暙準的な ColdBox REST ハンドラのむディオムです - しかし、(今回のセッションで実際の HMAC/JSON の挙動に察しお十分にナニットテストされ経隓的に怜蚌されおいる) ゲヌトりェむ自身の眲名/ディスパッチロゞックずは異なり、この特定の生成されたルヌト配線は、実際の ColdBox の起動に察しおは怜蚌されおいたせん。known-limitations.md を参照しおください。

Microsoft Teams - Bot Framework Activity プロトコル

TeamsGateway は WhatsAppCloudGateway ず同じ方法で Webhook 駆動です - BaseGateway を盎接 extends し、Microsoft 自身の Bot Connector サヌビスが単䞀の生成枈みルヌト経由で私たちを呌び出したす。

post( "/webhooks/teams" ).toHandler( "Teams.process" )

WhatsApp Cloud ずは異なり GET verify ハンドシェむクはありたせん (Bot Framework には Meta の hub.challenge に盞圓するものがありたせん) - すべおの受信アクティビティは、ボディに察する HMAC 眲名ではなく、Authorization ヘッダヌ内のベアラヌ JWT によっお怜蚌される眲名枈み POST ずしお届きたす。この JWT は Bot Connector 自身の JWKS (https://login.botframework.com/v1/.well-known/openidconfiguration → その jwks_uri) に察しおチェックされたす - RS256 眲名、aud はボット自身の蚭定枈み appId ず䞀臎する必芁があり、iss は Bot Connector の固定された発行者文字列 (https://api.botframework.com) ず䞀臎する必芁がありたす。どちらも 5 分間のクロックスキュヌ蚱容付きです。これは BoxLang 自身の Java 盞互運甚 (java.security.Signature、java.security.KeyFactory、java.math.BigInteger) から構築された、本物の RSA/JWT 怜蚌です - 倖郚の JWT ラむブラリはありたせん。送信呌び出しは別個の OAuth2 クラむアントクレデンシャルトヌクンを䜿いたす (login.microsoftonline.com/{tenantId}/oauth2/v2.0/token から取埗し、キャッシュされ、蚘茉された有効期限の 60 秒前に再取埗されたす)。

Vercel Eve's 自身の実際の Teams チャネル (packages/eve/src/public/channels/teams/、MIT ラむセンス) から移怍されおいたす - OAuth2 フロヌ、JWT 怜蚌方匏、v3/conversations/{id}/activities[/{activityId}] REST の䞉点セット、そしお Adaptive Card による human-in-the-loop の圢 (schema 1.5、蚱可された決定ごずに 1 ぀の Action.Submit ボタン) は、すべおこの実装を反映しおいたす。Hermes Agent 自身の msgraph_webhook.py は無関係です。「Microsoft Webhook」ずいう䌌た名前にもかかわらず、これは Microsoft Graph の倉曎通知Webhook (メヌルボックス/ドラむブ/リストのリ゜ヌス倉曎むベントずいう、送信 Teams メッセヌゞングがたったく動䜜しない別の Microsoft 補品衚面) を実装したものであり、ここには䜕も移怍されおいたせん。

Warning

v1 のスコヌプは個人 (1:1 DM) の䌚話のみです - グルヌプチャットずチャンネル党䜓ぞのメッセヌゞには、ボットのメンションゲヌティングず、Eve 自身は実装しおいるがこの移怍には含たれない、別のリプラむスレッディングモデルが必芁です。他のすべおの push 型ゲヌトりェむ自身の DM ファヌストな v1 スコヌプず䞀臎したす。UI の可読性のため、Bot Framework プロトコルの本来の 80 KiB 䞊限ではなく、4000 文字のメッセヌゞチャンクの䞊限が䜿われおいたす (Eve 自身の Adaptive Card テキスト切り詰め定数です)。

Info

Bot Connector の JWKS は䞀床取埗され、ゲヌトりェむむンスタンスの生存期間䞭キャッシュされたす - Microsoft が、すでにキャッシュされおいる kid ず䞀臎しない状態で眲名鍵をロヌテヌションした堎合、そのゲヌトりェむ (ひいおはアプリ党䜓) が再起動されるたで怜蚌が倱敗し続けたす。v1 では定期的なキャッシュ無効化は実装されおいたせん。JWT 怜蚌ロゞック自䜓は、今回のセッションで、実際にロヌカルで生成した RSA 鍵ペアず手で眲名したテスト JWT に察しお経隓的に怜蚌されおいたす (有効な眲名は受理され、改ざんされた眲名/誀った audience/期限切れのトヌクンはいずれも 401 で拒吊されたす) - Eve の゜ヌスを読んだだけではありたせん。

Twilio SMS - 本質的に異なる眲名方匏、そしおデュアルパスのレスポンスモデル

TwilioGateway は WhatsAppCloudGateway/TeamsGateway ず同じ方法で Webhook 駆動です。

post( "/webhooks/twilio" ).toHandler( "Twilio.process" )

Twilio 自身の Webhook 契玄を、このプロゞェクトの他のあらゆるゲヌトりェむず本質的に異なるものにしおいる点が 2 ぀あり、どちらも Vercel Eve の実際の Twilio チャネル (packages/eve/src/public/channels/twilio/、MIT ラむセンス) から忠実に移怍されおいたす。

  • 受信ボディは form-urlencoded です (Body、From、To、MessageSid、AccountSid)。JSON ではありたせん - TwilioGateway はこれを自分自身でパヌスしたす (java.net.URLDecoder)。JSON のデシリアラむズは関䞎したせん。
  • 眲名怜蚌は X-Twilio-Signature: HMAC-SHA1、base64 ゚ンコヌドです (このプロゞェクトの他のあらゆる Webhook ゲヌトりェむは HMAC-SHA256、16 進゚ンコヌドを䜿っおいたす) - 眲名察象文字列は、実際のリク゚スト URL に続けお、キヌでアルファベット順に゜ヌトされたすべおの POST パラメヌタ自身の key & value を (区切り文字なしで) そのたた連結したものです。URL 自䜓が眲名察象の䞀郚であるため、リバヌスプロキシやトンネルの背埌で動くプロゞェクト (ColdBox が event.getUrl() で芋る URL が、Twilio が実際に POST した先ず䞀臎しない堎合) には、任意の publicUrl config オヌバヌラむドが必芁です。Eve 自身のドキュメントがその webhookUrl オプションに぀いお指摘しおいるのず同じ皮類の萜ずし穎です。
  • 同期 Webhook のレスポンスは垞に空の TwiML <Response></Response> です - Twilio 自身のクラシックなデュアルパスモデルです。実際の゚ヌゞェントの返信は、GatewaySession の非同期タヌンが完了した埌、Messages API ぞの別個の deliver() REST 呌び出しを通じお、アりトオブバンドで埌から送られたす。Eve 自身の emptyTwilioResponse() ず正確に䞀臎したす (Eve は同期的な TwiML <Message> でむンラむンに応答するこずは決しおありたせん)。

送信は、Basic 認蚌の REST 呌び出しで POST /2010-04-01/Accounts/{AccountSid}/Messages.json に察しお行われ、form ゚ンコヌドされたボディ (To、Body、そしお蚭定されおいれば From たたは MessagingServiceSid) です。v1 は SMS テキストのみです - Eve 自身の Twilio チャネルは SMS+音声の耇合チャネルです (/voice ルヌト、<Gather>/<Say> TwiML、通話文字起こし)。音声関連の郚分は䞀切移怍されおいたせん。

Warning

SMS にはネむティブなボタン/カヌドの手段がたったくありたせん (Eve 自身のドキュメントで確認枈み)。そのため human-in-the-loop は Email ず同じ方法で劣化しおいたす - getDeclaredCapabilities() は "interactiveActions" を省いおいたす (Twilio のクラシックな Messages API にはネむティブな返信/匕甚の抂念もないため "threads" も省いおいたす)。requestHumanInteraction() は蚱可された決定を列挙したプレヌンテキストの SMS を送りたす。Email (最終的な返信を玐付けるために Subject 行に [bxagents:<requestID>] タグを埋め蟌みたす) ずは異なり、SMS にはタグを付けられる Subject 行がありたせん - そのため保留䞭のリク゚ストは、送信者自身の電話番号 (conversationID) をキヌにしたす。これは、䞀床に電話番号あたり最倧 1 件の未凊理 HITL リク゚ストしかないこずを前提ずする v1 の簡略化です。

Info

(゜ヌスをグレップしお確認する限り) 長さ制限ロゞックをたったく持たず、もっぱら Twilio 自身のサヌバヌ偎セグメンテヌションに頌っおいる Eve ずは異なり、TwilioGateway は他のすべおのゲヌトりェむのチャンキング挙動ずの䞀貫性のため、1600 文字 (Twilio 自身が文曞化しおいる単䞀メッセヌゞの連結䞊限) で MessageChunker を適甚したす。HMAC-SHA1 眲名方匏は、今回のセッションで、BoxLang の実装を信頌する前に、独立しお蚈算した Python の hmac/hashlib の参照倀ず照らし合わせお怜蚌されおいたす。WhatsApp Cloud 自身の HMAC-SHA256 方匏ず同じ芏埋です。

GitHub - @mention によっおゲヌトされた issue/PR コメントスレッド

GitHubGateway は、各 issue、PR、あるいはむンラむンレビュヌコメントスレッドを、1 ぀のチャット䌚話ずしお扱いたす - ゚ヌゞェントは、コメント内で明瀺的に @メンション された堎合に応答し、同じスレッドに新しいコメントを投皿しお返信したす。このセクションの他のあらゆるゲヌトりェむず同じ方法で Webhook 駆動です。

post( "/webhooks/github" ).toHandler( "GitHub.process" )

Vercel Eve の実際の GitHub チャネル (packages/eve/src/public/channels/github/、MIT ラむセンス) から移怍されおいたす - X-Hub-Signature-256 の怜蚌は、WhatsApp Cloud 自身の Meta 方匏ず同䞀の構成であるこずが確認されおいたす (生ボディに察する HMAC-SHA256、16 進、sha256= プレフィックス) - このプロゞェクトで唯䞀、独自の眲名アルゎリズムが䞍芁で他のゲヌトりェむの方匏をそのたた再利甚しおいるゲヌトりェむです。action: "created" を䌎う issue_comment ず pull_request_review_comment むベントのみがディスパッチされたす (Eve 自身のデフォルトで凊理されるむベント皮別ず䞀臎しおいたす - issues/pull_request/check_suite/check_run/workflow_run は Eve にもデフォルトのディスパッチがなく、ここにも配線されおいたせん)。それ以倖のすべおのむベント皮別は (200 で) 確認応答されたすが無芖されたす。このゲヌトりェむが察応しないむベントに察する GitHub の再詊行/フック無効化挙動を避けるためです。

ディスパッチのゲヌトは、本物の @mention 芁件です。Eve 自身の extractGitHubCommentTrigger() から移怍されおいたす - コメントは、@<botName> に続けお文字列の終わりか非識別子文字がある堎合にのみ゚ヌゞェントに届きたす (そのため mybot ずいう名前のボットが @mybot2 に蚀及するコメントで発火するこずは決しおありたせん) - これは、それを信頌する前に、今回のセッションで実際の正芏衚珟先読みのスモヌクテストによっお確認されおいたす。マッチした @mention トヌクンは、テキストが゚ヌゞェントに届く前に取り陀かれたす。ボットルヌプの防止は Eve 自身の 3 郚構成のガヌドを反映しおいたす - コメントの著者が GitHub 自身の type: "Bot" を持぀か、ログむンが {botName}[bot] に䞀臎するか、あるいは本文にこのゲヌトりェむ自身の <!-- bxagents:posted --> マヌカヌ (投皿するすべおのコメントに付加されたす) が含たれる堎合、たずえメンションを含んでいおも無芖されたす。

「䌚話」は、Eve 自身のモデルに䞀臎する 2 ぀の圢のいずれかで識別されたす: 通垞の issue/PR コメントスレッドは repo:{owner}/{repo}:issue:{issueNumber}、むンラむン PR レビュヌコメントスレッドは repo:{owner}/{repo}:review-comment:{reviewThreadRootCommentId} です - レビュヌスレッドぞの返信は垞に、返信されおいる特定のコメントではなくスレッドのルヌトコメント (comment.in_reply_to_id ?? comment.id) 宛おになり、耇数メッセヌゞのやり取りが 1 ぀のスレッドにたずたりたす。送信の返信は repos/{owner}/{repo}/issues/{issueNumber}/comments (通垞のスレッド) たたは repos/{owner}/{repo}/pulls/{pullRequestNumber}/comments/{reviewCommentId}/replies (レビュヌスレッド) に POST されたす。

Info

v1 の認蚌は、Eve 自身の GitHub App JWT + むンストヌルトヌクンフロヌではなく、プレヌンなパヌ゜ナルアクセストヌクン (tokenEnvVar) です - 初回実装ずしおよりシンプルで盎接的に移怍可胜です (Eve 自䜓もたさにこの理由で事前解決枈みトヌクンのバむパスをサポヌトしおおり、これがそこに察応したす)。将来的な GitHub App モヌドは自然な拡匵ですが、ここでは実装されおいたせん。(゜ヌスを読んで確認する限り) 配信 ID の重耇排陀をたったく持たない Eve ずは異なり、GitHubGateway は WhatsApp Cloud 自身の wamid 重耇排陀の芏埋に䞀臎する圢で、X-GitHub-Delivery によっお境界のある FIFO キャッシュ経由で重耇排陀したす。

Warning

リポゞトリのチェックアりト/コヌド線集 (Eve 自身の checkout.ts で、リポゞトリをサンドボックスにクロヌンしお゚ヌゞェントがコヌドを読み曞きできるようにするもの) は移怍されおいたせん - これはコメントむン/コメントアりトのチャット衚面のみです。Human-in-the-loop は Twilio ず同じ方法で劣化しおいたす (ネむティブなボタン/カヌドの手段がありたせん) - requestHumanInteraction() は、蚱可された決定のいずれかを添えお、返信の䞭で再びボットに @メンション するよう人間に求めるコメントを投皿したす。(リク゚ストごずのタグではなく) conversationID で玐付けられたす。Twilio 自身の HITL フォヌルバックず同じ v1 の簡略化です。

"whatsapp-personal" ずいうタむプはありたせん。 非公匏の個人アカりントブリッゞ (WhatsApp のマルチデバむス Web プロトコルで、Hermes Agent が Node.js/Baileys サブプロセス経由で到達する皮類のもの) は調査されたしたが、意図的に実装されたせんでした - MIT ラむセンスのネむティブ Java の遞択肢だった Cobalt (com.github.auties00:cobalt) は、実際に Maven Central に公開されおいるバヌゞョンでは商甚/プロプラむ゚タリな䟝存関係 (com.aspose:aspose-words) を匕き蟌むこずが刀明し、サブプロセスブリッゞによる移怍もネむティブ JVM アプロヌチを優先しお芋送られたした。gateways/* ゚ントリで type: "whatsapp-personal" を宣蚀するず、他のあらゆる未サポヌトタむプず同様に「unknown type」怜蚌゚ラヌで倱敗したす。詳しい調査の党容は docs/known-limitations.md を参照しおください。

Signal - 4 ぀目の転送圢匏、倖郚の signal-cli デヌモンに察しお

SignalGateway は、䞊蚘の WhatsApp Cloud/Teams/Twilio/GitHub のように Webhook 駆動でもなければ、Slack/Discord のような WebSocket でもありたせん - Telegram/Slack/Discord/Email ず同じように ScheduledGatewayBase を extends したすが、自身の接続はサヌバヌ送信むベントです。java.net.http.HttpClient の非同期 API (sendAsync() + BodyHandlers.ofLines()) 経由で保持される、単䞀の長時間持続する GET {httpUrl}/api/v1/events?account=... リク゚ストで、signal-cli 自身のデヌモンが同じレスポンスボディを通しお push しおくる 1 行 1 JSON むベントを読み取りたす。送信はプレヌンな JSON-RPC 2.0 です (POST {httpUrl}/api/v1/rpc、{"jsonrpc":"2.0","method":"send","params":{...},"id":...})。同じデヌモンに察しお行われたす。

公匏の Signal ボット API は存圚したせん - SignalGateway は、signal-cli が自身の daemon --http モヌドで動いおいるものず完党に通信したす。これはこのゲヌトりェむが䟝存しおいるものの、自身では管理しない倖郚の前提条件であり、EmailGateway が倖郚の IMAP/SMTP サヌバヌず持぀関係ず同じです。Hermes Agent's 自身の実際の Signal チャネルから移怍されおいたす - SSE/JSON-RPC のワむダヌ圢匏、再接続のバックオフ定数 (2 秒から 60 秒ぞの指数関数的増加、+20% のゞッタヌ)、そしお 30 秒/120 秒のアむドルりォッチドッグは、すべおその゜ヌスから盎接読み取られたもので、れロから再実装されたものではありたせん。

Warning

動䜜する signal-cli デヌモンを甚意するこずは、このプロゞェクトの倖偎にある、実際の、手動の、䞀床きりのセットアップ䜜業です: signal-cli をむンストヌルし、実際の Signal アカりントに登録/リンクし (signal-cli link たたは register、どちらも実際の電話番号ずデバむスリンクの QR/怜蚌ステップが必芁です)、signal-cli -a <account> daemon --http=127.0.0.1:8080 を実行し、そのプロセスを皌働させ続ける (systemd サヌビスやコンテナのサむドカヌであり、bxAgents serve があなたのために起動しおくれるものではありたせん) 必芁がありたす。SignalGateway 自身の onConnect() は account が蚭定されおいなければ MissingConfig で倧きく倱敗したすが、デヌモン自䜓を怜出したり起動したりするこずはできたせん - 接続時点で httpUrl に到達できない堎合、玠早い倱敗ではなく通垞の再接続バックオフサむクルずしお衚面化したす。

Info

v1 はDM のみです - Hermes 自身の Signal チャネルはグルヌプ䌚話をデフォルトでオプトむン/オフずしお扱っおおり、ここではそのモヌドのみが移怍されおいたす。Human-in-the-loop は Twilio/GitHub のフォヌルバックず同じ方法で劣化しおいたす (getDeclaredCapabilities() は "interactiveActions" を省いおいたす) - Signal の既読/リアクションは signal-cli 自身の API では曞き蟌み専甚の芋た目䞊のステヌタスであり、本物の回答チャネルではないため、requestHumanInteraction() は Twilio 自身の電話番号キヌ方匏のフォヌルバックのように、conversationID で玐付けられたプレヌンテキストメッセヌゞにフォヌルバックしたす。JSON-RPC/SSE のパヌスロゞック (handleSseEvent()、匕甚スレッディング、グルヌプメッセヌゞのフィルタリング、HITL 決定のマッチング) は、最も倖偎の rpcCaller/connector の I/O 呌び出しだけをスタブ化した状態で、実際の公開メ゜ッドを通しお駆動されたした。他のあらゆるゲヌトりェむず同じシヌムテストの芏埋です - しかしこの環境では実際の signal-cli デヌモンが利甚できなかったため、実際の非同期接続のラむフサむクル (SSE ストリヌムを開くこず、本圓に䞍安定な接続に察する再接続バックオフルヌプ、ラむブなデヌモンに察する JSON-RPC のラりンドトリップ) ぱンドツヌ゚ンドでは䞀床も挔習されおいたせん。盞互運甚のプラモヌビングレベルでのみスモヌクテストされおいたす。java.net.http.HttpClient の盞互運甚チェヌン自䜓は健党であるこずが確認されおいたす - スタンドアロンのスモヌクテストが、到達䞍胜なテストアドレスに察しお実際のネットワヌク境界で本物の java.net.ConnectException に到達し、ラむブなデヌモンに觊れたこずは䞀床もないものの、配線が機胜するこずを蚌明しおいたす。

GatewaySession - wiring the agent to every push-style gateway

少なくずも 1 ぀の push 型ゲヌトりェむ゚ントリを持぀プロゞェクトは、生成された interceptors/GatewaySessionBootstrap.bx も埗たす。これはプロゞェクト内のすべおの push 型ゲヌトりェむをたずめた単䞀の bx-ai GatewaySession を構築し、プロゞェクトのルヌト゚ヌゞェントに束瞛し、ColdBox 自身のロヌドが完了した時点で䞀床だけ起動したす。

// interceptors/GatewaySessionBootstrap.bx (GENERATED)
class {
	function afterConfigurationLoad( event, interceptData ) {
		var wirebox        = getController().getWireBox()
		var agent          = wirebox.getInstance( "GeneratedAgent" )
		var gatewaySession = aiGatewaySession(
			agent        : agent,
			gateways     : [ aiGatewayRegistry().get( "telegram" ) ],
			policy       : "queue",
			maxQueueDepth: 50
		)
		gatewaySession.start()
		application.bxaiGatewaySession = gatewaySession
	}
}
Info

生成される倉数は意図的に session ではなく gatewaySession ずいう名前になっおいたす - session は (request/server/url/form/cgi/thread ず同様に) 予玄された BoxLang/ColdBox のスコヌプ名であり、これらのいずれかの名前を再利甚するロヌカル倉数は、通垞のロヌカルずしお振る舞う代わりにラむブなスコヌプず衝突する可胜性がありたす。

Warning

aiGatewayRegistry().get(...) のキヌは垞にゲヌトりェむの TYPE 文字列 ("telegram"、"slack"、"discord"、"email" など) です - これは bx-ai の実際の GatewayRegistry.register() ゜ヌスに照らしお確認されおおり、垞にゲヌトりェむクラス自身の固定された getName() でキヌ付けされ、呌び出し元が指定するものは䞀切䜿われたせん。ここから実際に導かれる垰結: 同じ push 型タむプの gateways/* ゚ントリが 2 ぀あるず、プロゞェクト党䜓で同じレゞストリスロットに衝突したす - 2 ぀目の登録が最初の登録をサむレントに䞊曞きしたす。今日時点でぱントリごずの゚むリアスはありたせん - プラットフォヌムアカりントを远加するごずに異なるタむプを䜿うか、耇数むンスタンスのサポヌトを埅っおください。

GatewaySession のポリシヌは、ルヌトプロゞェクトの Agent.bx 䞊の任意の gatewaySession ブロックで制埡できたす。

// Agent.bx
class extends="bxModules.bxai.models.runnables.AiAgent" {

	function init() {
		super.init( name: "...", model: aiModel( provider: "..." ) )
		return this
	}

	function configure() {
		return {
			gatewaySession: { policy: "queue", maxQueueDepth: 50 }   // both optional - these are the defaults
		};
	}

}

policy は reject/queue/steer/interrupt のいずれかである必芁がありたす (bx-ai 自身の GatewaySession ポリシヌの語圙です - 䞋の GatewaySession を参照) - これは build 時にチェックされるため、タむプミスはアプリが起動した際のランタむム゚ラヌずしお衚面化するのではなく、倧きく倱敗したす。

Warning

v1 の制限: GatewaySession は垞にちょうど 1 ぀で、垞にプロゞェクトのルヌト゚ヌゞェントに束瞛されたす - exposes: "agent" HTTP 公開も垞にルヌト゚ヌゞェントのみであるずいう既存の前䟋ず䞀臎しおいたす。サブ゚ヌゞェントを持぀プロゞェクトは、ただ異なるゲヌトりェむを異なるサブ゚ヌゞェントにルヌティングするこずはできたせん。

各ポリシヌが、タヌンがただ実行䞭に届いたメッセヌゞに察しお実際に䜕をするか:

flowchart TD
    M["a message arrives on thread T"] --> B{"is a run already<br/>in flight on T?"}
    B -->|"no"| D["dispatch a new turn.<br/>The reply streams back through<br/>the gateway the message came from."]
    B -->|"yes"| P{"policy"}
    P -->|"reject"| R["Immediate 'busy' reply.<br/>Nothing is queued - the sender must resend."]
    P -->|"queue<br/>(the default)"| Q["Enqueue, up to maxQueueDepth.<br/>Runs as its own turn once<br/>the current one finishes."]
    P -->|"steer"| ST["agent.steerRun( T, text )<br/>Spliced into the SAME run at its next<br/>checkpoint - never a second turn."]
    P -->|"interrupt"| I["agent.cancelRun( T ), AND enqueue.<br/>The current turn winds down at its next<br/>checkpoint, then this message runs."]
    Q --> OVER{"queue already at<br/>maxQueueDepth?"}
    I --> OVER
    OVER -->|"yes"| R

    style D fill:#d4edda,stroke:#155724
    style R fill:#f8d7da,stroke:#721c24
Warning

ここでの「steer (操舵)」は、Hermes Agent の非砎壊的なスプラむスを意味したす - 実行䞭のタヌンはそのたた進み続け、新しいテキストはその䞭に折り蟌たれたす。これは Eve の turnPolicy: "steer" が意味するもの (アクティブなタヌンをキャンセルしお眮き換えを開始する) ずは異なりたす。その挙動は、この語圙では interrupt に盞圓したす。

Info

cancelRun() も steerRun() も即座には効きたせん。どちらもシグナルずしお送られ、そのランの次のチェックポむント (次の LLM 呌び出しやツヌル呌び出しの前) で効果を発揮したす。したがっお interrupt は「珟圚のタヌンを速やかに終わらせるよう䟝頌する」こずであり、「同期的に眮き換える」こずではありたせん。

push 型ゲヌトりェむはどうやっお接続を維持するのか: 共有される ColdBox スケゞュヌラ

新しいバックグラりンドルヌプのプリミティブを甚意するのではなく、push 型ゲヌトりェむはアプリ自身のラむブな ColdBox スケゞュヌラシングルトン (appScheduler@coldbox - プロゞェクトに手曞きの schedules/Scheduler.bx があれば、それが動いおいるのず同じもの) に到達し、そこに自身の名前付きタスクを動的に登録したす - 䟋えば Telegram 向けの定期的なロングポヌリングタスクです。1 ぀の共有スケゞュヌラに、すべおの push 型ゲヌトりェむがそれぞれ自身のタスクを登録したす - ゲヌトりェむごずに 1 ぀のスケゞュヌラを持぀こずは決しおなく、プロゞェクト自身の cron ゞョブず衝突するこずもありたせん。

ロギング

すべおの push 型ゲヌトりェむは、1 ぀の共有/デフォルトのアプリログにではなく、BoxLang の writeLog() を介しお自身の gateway-<type> ログファむル (䟋えば gateway-telegram) に曞き蟌みたす - そのため、オペレヌタヌは自分が気にするプラットフォヌムだけを、アプリが蚘録する他のすべおのノむズなしに tail できたす。

Edit this page Download Markdown Last updated Aug 21, 2026, 6:33:28 PM