Building plugins
ãã£ã³ãã« Plugin ã®æ§ç¯
ãã®ã¬ã€ãã§ã¯ãOpenClaw ãã¡ãã»ãŒãžã³ã°ãã©ãããã©ãŒã ã«æ¥ç¶ãããã£ãã« Plugin ãæ§ç¯ããŸããDM ã®ã»ãã¥ãªãã£ããã¢ãªã³ã°ãè¿ä¿¡ã®ã¹ã¬ããåãéä¿¡ã¡ãã»ãŒãžã³ã°ãæ±ããŸãã
Plugin ãæ ããã®
ãã£ãã« Plugin ã¯éä¿¡ãç·šéããªã¢ã¯ã·ã§ã³ã®ããŒã«ãå®è£
ããŸãããã³ã¢ãå
±æã®
message ããŒã«ã 1 ã€æäŸããŸããPlugin ã¯ä»¥äžãæ
ããŸãã
- èšå® - ã¢ã«ãŠã³ãã®è§£æ±ºãšã»ããã¢ãããŠã£ã¶ãŒã
- ã»ãã¥ãªã㣠- DM ããªã·ãŒãšèš±å¯ãªã¹ã
- ãã¢ãªã³ã° - DM æ¿èªãããŒ
- ã»ãã·ã§ã³ææ³ - ãããã€ããŒåºæã®äŒè©± ID ãããŒã¹ãã£ãããã¹ã¬ãã IDã 芪ãã©ãŒã«ããã¯ã«ãããã³ã°ããæ¹æ³
- éä¿¡ - ãã©ãããã©ãŒã ãžã®ããã¹ããã¡ãã£ã¢ãæç¥šã®éä¿¡
- ã¹ã¬ããå - è¿ä¿¡ãã¹ã¬ããåããæ¹æ³
- Heartbeat å ¥åäžè¡šç€º - Heartbeat ã®é ä¿¡å ã«å¯Ÿããããªãã·ã§ã³ã®å ¥åäžïŒããžãŒã·ã°ãã«
ã³ã¢ã¯ãå
±æã¡ãã»ãŒãžããŒã«ãããã³ããã®æ¥ç¶ãå€åŽã®ã»ãã·ã§ã³ããŒåœ¢åŒã
æ±çšã® :thread: 管çãããã³ãã£ã¹ããããæ
ããŸãã
ã¡ãã»ãŒãžã¢ããã¿ãŒ
openclaw/plugin-sdk/channel-outbound ã® defineChannelMessageAdapter ã䜿çšããŠã
message ã¢ããã¿ãŒãå
¬éããŸãããã€ãã£ããã©ã³ã¹ããŒããå®éã«ãµããŒãããã
æ°žç¶çãªæçµéä¿¡æ©èœã®ã¿ã宣èšãããã€ãã£ãåŽã®å¯äœçšãšè¿ãããã¬ã·ãŒãã蚌æãã
ã³ã³ãã©ã¯ããã¹ãã§è£ä»ããŠãã ãããããã¹ãïŒã¡ãã£ã¢éä¿¡ã¯ãåŸæ¥ã® outbound
ã¢ããã¿ãŒã䜿çšãããã®ãšåããã©ã³ã¹ããŒã颿°ã«æ¥ç¶ããŸããå®å
šãª API ã³ã³ãã©ã¯ãã
æ©èœãããªã¯ã¹ãã¬ã·ãŒãèŠåãã©ã€ããã¬ãã¥ãŒã®ç¢ºå®ãå信確èªããªã·ãŒããã¹ãã
ç§»è¡è¡šã«ã€ããŠã¯ããã£ãã«éä¿¡ APIãåç
§ããŠãã ããã
æ¢åã® outbound ã¢ããã¿ãŒã«é©åãªéä¿¡ã¡ãœãããšæ©èœã¡ã¿ããŒã¿ããã§ã«ããå Žåã¯ã
å¥ã®ããªããžãææžããããcreateChannelMessageAdapterFromOutbound(...) ã§
message ã¢ããã¿ãŒã掟çãããŠãã ãããã¢ããã¿ãŒã®é信㯠MessageReceipt å€ãè¿ããŸãã
åŸæ¥ã® ID ã«ã€ããŠã¯ã䞊åã® messageIds ãã£ãŒã«ããä¿æããã®ã§ã¯ãªãã
listMessageReceiptPlatformIds(...) ãŸãã¯
resolveMessageReceiptPrimaryId(...) ã§æŽŸçãããŠãã ããã
ã©ã€ãæ©èœãšãã¡ã€ãã©ã€ã¶ãŒæ©èœãæ£ç¢ºã«å®£èšããŠãã ãããã³ã¢ã¯ãããã䜿çšã㊠ãã£ãã«ãå®è¡ã§ããããšã倿ããŸãã宣èšãããåäœãšå®éã®åäœã®ããã¯ã ã³ã³ãã©ã¯ããã¹ãã®å€±æã«ãªããŸãã
| ãµãŒãã§ã¹ | å€ |
|---|---|
message.live.capabilities |
draftPreview, previewFinalization, progressUpdates, nativeStreaming, quietFinalization |
message.live.finalizer.capabilities |
finalEdit, normalFallback, discardPending, previewReceipt, retainOnAmbiguousFailure |
ãã©ãããã¬ãã¥ãŒããã®å Žã§ç¢ºå®ãããã£ãã«ã§ã¯ãã©ã³ã¿ã€ã ããžãã¯ã
defineFinalizableLivePreviewAdapter(...) ãš
deliverWithFinalizableLivePreviewAdapter(...) ãä»ããŠåŠçãã宣èšããæ©èœã
verifyChannelMessageLiveCapabilityAdapterProofs(...) ããã³
verifyChannelMessageLiveFinalizerProofs(...) ã®ãã¹ãã§è£ä»ããŠãã ãããããã«ããã
ãã€ãã£ããã¬ãã¥ãŒã鲿ãç·šéããã©ãŒã«ããã¯ïŒä¿æãã¯ãªãŒã³ã¢ãããã¬ã·ãŒãã®åäœã
æé»ã«ãããããšãé²ããŸãã
ãã©ãããã©ãŒã ãžã®å信確èªãé
å»¶ãããåä¿¡åŽã¯ã確èªã¿ã€ãã³ã°ãã¢ãã¿ãŒåºæã®ç¶æ
ã«
é ãã®ã§ã¯ãªããmessage.receive.defaultAckPolicy ãš supportedAckPolicies ã
宣èšããŠãã ããã宣èšãããã¹ãŠã®ããªã·ãŒã
verifyChannelMessageReceiveAckPolicyAdapterProofs(...) ã§ç¶²çŸ
ããŠãã ããã
createChannelTurnReplyPipelineãdispatchInboundReplyWithBaseã
recordInboundSessionAndDispatchReply ãªã©ã®åŸæ¥ã®è¿ä¿¡ãã«ããŒã¯ã
äºæãã£ã¹ãããã£ãŒåãã«åŒãç¶ãå©çšã§ããŸããæ°ãããã£ãã«ã³ãŒãã§ã¯äœ¿çšããªãã§ãã ããã
代ããã«ãopenclaw/plugin-sdk/channel-outbound ã® message ã¢ããã¿ãŒãã¬ã·ãŒãã
åä¿¡ïŒéä¿¡ã©ã€ããµã€ã¯ã«ãã«ããŒããå§ããŠãã ããã
åä¿¡ã€ã³ã°ã¬ã¹ïŒå®éšçïŒ
åä¿¡èªå¯ãç§»è¡ãããã£ãã«ã¯ãã©ã³ã¿ã€ã ã®åä¿¡ãã¹ããå®éšçãª
openclaw/plugin-sdk/channel-ingress-runtime ãµããã¹ã䜿çšã§ããŸãã
ããã¯ãã©ãããã©ãŒã ã®ãã¡ã¯ããçã®èš±å¯ãªã¹ããã«ãŒãèšè¿°åãã³ãã³ãã®ãã¡ã¯ãã
ã¢ã¯ã»ã¹ã°ã«ãŒãèšå®ãåãåããéä¿¡è
ïŒã«ãŒãïŒã³ãã³ãïŒã¢ã¯ãã£ããŒã·ã§ã³ã®å°åœ±ãšã
é åºä»ããããã€ã³ã°ã¬ã¹ã°ã©ããè¿ããŸããäžæ¹ããã©ãããã©ãŒã ã®æ€çŽ¢ãšå¯äœçšã¯
Plugin å
ã«æ®ããŸããPlugin ã®ã¢ã€ãã³ãã£ãã£æ£èŠåã¯ããªãŸã«ããŒã«æž¡ãèšè¿°åå
ã«
ä¿æããŠãã ããã解決æžã¿ã®ç¶æ
ãæ±ºå®ãããçã®ç
§åå€ãã·ãªã¢ã©ã€ãºããªãã§ãã ããã
API èšèšãæææš©ã®å¢çããã¹ãèŠä»¶ã«ã€ããŠã¯ã
ãã£ãã«ã€ã³ã°ã¬ã¹ APIãåç
§ããŠãã ããã以åã®
openclaw/plugin-sdk/channel-ingress ãµããã¹ã¯ããµãŒãããŒãã£ãŒ Plugin åãã®
éæšå¥šäºæãã¡ãµãŒããšããŠåŒãç¶ããšã¯ã¹ããŒããããŸãã
å ¥åäžã€ã³ãžã±ãŒã¿ãŒ
ãã£ãã«ãåä¿¡è¿ä¿¡ä»¥å€ã§ãå
¥åäžã€ã³ãžã±ãŒã¿ãŒããµããŒãããå Žåã¯ã
ãã£ãã« Plugin ã§ heartbeat.sendTyping(...) ãå
¬éããŠãã ããã
ã³ã¢ã¯ Heartbeat ã¢ãã«ã®å®è¡éå§åã«ã解決æžã¿ã® Heartbeat é
ä¿¡å
ãæå®ããŠãããåŒã³åºãã
å
±æã®å
¥åäžç¶æ
ããŒãã¢ã©ã€ãïŒã¯ãªãŒã³ã¢ããã©ã€ããµã€ã¯ã«ã䜿çšããŸãã
ãã©ãããã©ãŒã ã§æç€ºçãªåæ¢ã·ã°ãã«ãå¿
èŠãªå Žåã¯ãheartbeat.clearTyping(...) ã远å ããŠãã ããã
ã¡ãã£ã¢ãœãŒã¹ãã©ã¡ãŒã¿ãŒ
ãã£ãã«ãã¡ãã£ã¢ãœãŒã¹ãä¿æããã¡ãã»ãŒãžããŒã«ãã©ã¡ãŒã¿ãŒã远å ããå Žåã¯ã
plugin.actions.describeMessageTool(...).mediaSourceParams ãéããŠ
ãããã®ãã©ã¡ãŒã¿ãŒåãå
¬éããŠãã ãããã³ã¢ã¯ãã®æç€ºçãªãªã¹ããã
ãµã³ãããã¯ã¹ãã¹ã®æ£èŠåãšéä¿¡ã¡ãã£ã¢ã¢ã¯ã»ã¹ã®ããªã·ãŒã«äœ¿çšããŸãã
ãã®ãããPlugin ã¯ãããã€ããŒåºæã®ã¢ãã¿ãŒãæ·»ä»ãã¡ã€ã«ãã«ããŒç»åã®ãã©ã¡ãŒã¿ãŒçšã«ã
å
±æã³ã¢ãžç¹å¥ãªåŠçã远å ããå¿
èŠããããŸããã
{ "set-profile": ["avatarUrl", "avatarPath"] } ã®ãããªã¢ã¯ã·ã§ã³ããŒä»ããããã
æšå¥šããŸããããã«ãããç¡é¢ä¿ãªã¢ã¯ã·ã§ã³ãå¥ã®ã¢ã¯ã·ã§ã³ã®ã¡ãã£ã¢åŒæ°ãç¶æ¿ããŸããã
å
¬éãããã¹ãŠã®ã¢ã¯ã·ã§ã³ã§æå³çã«å
±æãããã©ã¡ãŒã¿ãŒã«ã¯ããã©ããé
åãåŒãç¶ã䜿çšã§ããŸãã
ãã©ãããã©ãŒã åŽã®ã¡ãã£ã¢ååŸçšã«äžæçãªå
¬é URL ãå
¬éããå¿
èŠããããã£ãã«ã¯ã
Plugin ã®ç¶æ
ã¹ãã¢ãšãšãã« openclaw/plugin-sdk/outbound-media ã®
createHostedOutboundMediaStore(...) ã䜿çšã§ããŸãããã©ãããã©ãŒã ã®ã«ãŒãè§£æãš
ããŒã¯ã³ã®åŒ·å¶ã¯ãã£ãã« Plugin å
ã«ä¿æããŠãã ãããå
±æãã«ããŒãæ
ãã®ã¯ã
ã¡ãã£ã¢ã®èªã¿èŸŒã¿ãæå¹æéã¡ã¿ããŒã¿ããã£ã³ã¯è¡ãã¯ãªãŒã³ã¢ããã®ã¿ã§ãã
ãã€ãã£ããã€ããŒãã®æŽåœ¢
ãã£ãã«ã§ message(action="send") ã«å¯Ÿãããããã€ããŒåºæã®æŽåœ¢ãå¿
èŠãªå Žåã¯ã
actions.prepareSendPayload(...) ãæšå¥šããŸãããã€ãã£ãã«ãŒãããããã¯ãåã蟌ã¿ã
ãã®ä»ã®æ°žç¶ããŒã¿ã payload.channelData.<channel> é
äžã«é
眮ãã
ã³ã¢ãéä¿¡ïŒã¡ãã»ãŒãžã¢ããã¿ãŒãä»ããŠéä¿¡ããããã«ããŸãã
actions.handleAction(...) ãéä¿¡ã«äœ¿çšããã®ã¯ãã·ãªã¢ã©ã€ãºããŠå詊è¡ã§ããªã
ãã€ããŒãã«å¯Ÿããäºæãã©ãŒã«ããã¯ã®å Žåã®ã¿ã«ããŠãã ããã
ã»ãã·ã§ã³äŒè©±ææ³
ãã©ãããã©ãŒã ãäŒè©± ID å
ã«è¿œå ã®ã¹ã³ãŒããä¿åããå Žåã¯ããã®è§£æã
messaging.resolveSessionConversation(...) ã䜿çšã㊠Plugin å
ã«ä¿æããŠãã ããã
ããã¯ãrawId ãããŒã¹äŒè©± IDããªãã·ã§ã³ã®ã¹ã¬ãã IDãæç€ºçãª
baseConversationIdãä»»æã® parentConversationCandidates ã«ãããã³ã°ããããã®
æšæºããã¯ã§ããparentConversationCandidates ãè¿ãå Žåã¯ãæãéå®çãªèŠªãã
æãåºç¯ãªèŠªïŒããŒã¹äŒè©±ã®é ã«äžŠã¹ãŠãã ããã
messaging.resolveParentConversationCandidates(...) ã¯ãæ±çšïŒçã® ID ã«å ããŠ
芪ãã©ãŒã«ããã¯ã®ã¿ãå¿
èŠãšãã Plugin åãã®ãéæšå¥šã®äºæãã©ãŒã«ããã¯ã§ãã
äž¡æ¹ã®ããã¯ãååšããå Žåãã³ã¢ã¯æåã«
resolveSessionConversation(...).parentConversationCandidates ã䜿çšãã
æšæºããã¯ãããããçç¥ããå Žåã«ã®ã¿
resolveParentConversationCandidates(...) ã«ãã©ãŒã«ããã¯ããŸãã
ãã£ãã«ã¬ãžã¹ããªã®èµ·ååã«åãè§£æãå¿
èŠãªãã³ãã« Plugin ã¯ã
äžèŽãã resolveSessionConversation(...) ãšã¯ã¹ããŒããæã€ãããã¬ãã«ã®
session-key-api.ts ãã¡ã€ã«ãå
¬éã§ããŸãïŒFeishu ããã³ Telegram Plugin ãåç
§ïŒã
ã³ã¢ã¯ãã©ã³ã¿ã€ã Plugin ã¬ãžã¹ããªããŸã å©çšã§ããªãå Žåã«ã®ã¿ã
ãã®ããŒãã¹ãã©ããã»ãŒããªãµãŒãã§ã¹ã䜿çšããŸãã
Plugin ã³ãŒãã§ã«ãŒã圢åŒã®ãã£ãŒã«ããæ£èŠåãããåã¹ã¬ãããšãã®èŠªã«ãŒããæ¯èŒããã
ãŸã㯠{ channel, to, accountId, threadId } ããå®å®ããéè€æé€ããŒãæ§ç¯ããå¿
èŠãããå Žåã¯ã
openclaw/plugin-sdk/channel-route ã䜿çšããŠãã ããããã®ãã«ããŒã¯æ°å€ã®ã¹ã¬ãã ID ã
ã³ã¢ãšåãæ¹æ³ã§æ£èŠåãããããã¢ãããã¯ãª String(threadId) ã®æ¯èŒããåªå
ããŠãã ããã
ãããã€ããŒåºæã®å®å
ææ³ãæã€ Plugin ã¯
messaging.resolveOutboundSessionRoute(...) ãå
¬éããã³ã¢ãããŒãµãŒã·ã ãªãã§
ãããã€ããŒãã€ãã£ãã®ã»ãã·ã§ã³ãšã¹ã¬ããã®ã¢ã€ãã³ãã£ãã£ãååŸã§ããããã«ããŠãã ããã
ã¢ã«ãŠã³ãã¹ã³ãŒãã®äŒè©±ãã€ã³ãã£ã³ã°ã®ãµããŒã
ãã£ãã«ãæ±çšã®çŸåšã®äŒè©±ãã€ã³ãã£ã³ã°ããµããŒãããå Žåã¯ã
conversationBindings.supportsCurrentConversationBinding ãèšå®ããŠãã ããã
createChatChannelPlugin(...) ã¯ããã®éçæ©èœãããã©ã«ãã§ true ã«èšå®ããŸãã
èšå®æžã¿ã¢ã«ãŠã³ãã«ãã£ãŠãµããŒãç¶æ³ãç°ãªãå Žåã¯ã
conversationBindings.isCurrentConversationBindingSupported({ accountId }) ãå®è£
ããŠãã ããã
ã³ã¢ã¯ãéçæ©èœãæå¹ãªå Žåã«ã®ã¿ããã®åæããã¯ãè©äŸ¡ããŸããfalse ãè¿ããšã
ãã®ã¢ã«ãŠã³ãã§ã¯æ±çšã®çŸåšã®äŒè©±ã«é¢ããæ©èœããã€ã³ããæ€çŽ¢ãäžèŠ§è¡šç€ºãæŽæ°ã
ãã€ã³ãè§£é€ã®æäœãå©çšã§ããªããªããŸããããã¯ãçç¥ãããšãéçæ©èœããã¹ãŠã®ã¢ã«ãŠã³ãã«é©çšãããŸãã
ãã§ã«èªã¿èŸŒãŸããŠããã¢ã«ãŠã³ãèšå®ãŸãã¯ã©ã³ã¿ã€ã ç¶æ
ããçµæã解決ããŠãã ããã
ãã®ããã¯ãå¶åŸ¡ããã®ã¯æ±çšã®çŸåšã®äŒè©±ãã€ã³ãã£ã³ã°ã®ã¿ã§ããèšå®æžã¿ã®ãã€ã³ãã£ã³ã°èŠåãã
Plugin ãææããã»ãã·ã§ã³ã«ãŒãã£ã³ã°ã眮ãæãããã®ã§ã¯ãããŸããã
ã³ã³ãã©ã¯ããã¹ãã§ã¯ãopenclaw/plugin-sdk/channel-core ããšã¯ã¹ããŒããã
ChannelPlugin["conversationBindings"] ã³ã³ãã©ã¯ããéããŠãå°ãªããšã 1 ã€ã®
ãµããŒã察象ã¢ã«ãŠã³ããš 1 ã€ã®éãµããŒãã¢ã«ãŠã³ããç¶²çŸ
ããŠãã ããã
æ¿èªãšãã£ãã«æ©èœ
ã»ãšãã©ã®ãã£ãã« Plugin ã§ã¯ãæ¿èªåºæã®ã³ãŒãã¯å¿
èŠãããŸãããã³ã¢ãåäžãã£ããã®
/approveãå
±æã®æ¿èªãã¿ã³ãã€ããŒããæ±çšãã©ãŒã«ããã¯é
ä¿¡ãæ
ããŸãã
ChannelPlugin.approvals ã¯åé€ãããŸããã代ããã«ãæ¿èªã®é
ä¿¡ïŒãã€ãã£ãïŒã¬ã³ããªã³ã°ïŒèªèšŒã®
ãã¡ã¯ãã 1 ã€ã® approvalCapability ãªããžã§ã¯ãã«é
眮ããŠãã ããã
plugin.auth ã¯ãã°ã€ã³ïŒãã°ã¢ãŠãå°çšã§ããã³ã¢ã¯ãã®ãªããžã§ã¯ãããæ¿èªèªèšŒããã¯ãèªã¿åããªããªããŸããã
approvalCapability.delivery ã¯ãã€ãã£ãæ¿èªã®ã«ãŒãã£ã³ã°ãŸãã¯ãã©ãŒã«ããã¯ææ¢ã®å Žåã®ã¿ã«äœ¿çšãã
approvalCapability.render ã¯å
±æã¬ã³ãã©ãŒã§ã¯ãªãã«ã¹ã¿ã æ¿èªãã€ããŒããæ¬åœã«å¿
èŠãªãã£ãã«ã§ã®ã¿äœ¿çšããŠãã ããã
æ¿èªèªèšŒ
approvalCapability.authorizeActorActionãšapprovalCapability.getActionAvailabilityStateãæšæºã®æ¿èªèªèšŒã·ãŒã ã§ãã- åäžãã£ããã®æ¿èªèªèšŒã®å¯çšæ§ã«ã¯
getActionAvailabilityStateã䜿çšããŠãã ããã ãã€ãã£ãé ä¿¡ãç¡å¹ãªå Žåã§ããèšå®æžã¿ã®æ¿èªè ã/approveãå©çšã§ããç¶æ ãç¶æããŠãã ããã é ä¿¡ïŒã»ããã¢ããã®ã¬ã€ãã³ã¹ã«ã¯ã代ããã«ãã€ãã£ãã®éå§ãµãŒãã§ã¹ç¶æ ã䜿çšããŠãã ããã - ãã£ãã«ããã€ãã£ãã®å®è¡æ¿èªãå
¬éããå Žåã¯ãéå§ãµãŒãã§ã¹ïŒãã€ãã£ãã¯ã©ã€ã¢ã³ãã®ç¶æ
ã
åäžãã£ããã®æ¿èªèªèšŒãšç°ãªããšãã«ã
approvalCapability.getExecInitiatingSurfaceStateã䜿çšããŠãã ããã ã³ã¢ã¯ãã®å®è¡åºæã®ããã¯ã䜿çšããŠenabledãšdisabledãåºå¥ãã éå§å ãã£ãã«ããã€ãã£ãã®å®è¡æ¿èªããµããŒããããã©ããã倿ãã ãã®ãã£ãã«ããã€ãã£ãã¯ã©ã€ã¢ã³ãã®ãã©ãŒã«ããã¯ã¬ã€ãã³ã¹ã«å«ããŸãã äžè¬çãªã±ãŒã¹ã§ã¯ãcreateApproverRestrictedNativeApprovalCapability(...)ããããèšå®ããŸãã - ãã£ãã«ãæ¢åã®èšå®ããå®å®ããææè
çžåœã® DM ã¢ã€ãã³ãã£ãã£ãæšæž¬ã§ããå Žåã¯ã
openclaw/plugin-sdk/approval-runtimeã®createResolvedApproverActionAuthAdapterã䜿çšããæ¿èªåºæã®ã³ã¢ããžãã¯ã远å ããã« åäžãã£ããã®/approveãå¶éããŠãã ããã - ã«ã¹ã¿ã æ¿èªèªèšŒãæå³çã«åäžãã£ããã®ãã©ãŒã«ããã¯ã®ã¿ãèš±å¯ããå Žåã¯ã
openclaw/plugin-sdk/approval-auth-runtimeã®markImplicitSameChatApprovalAuthorization({ authorized: true })ãè¿ããŠãã ããã ãã以å€ã®å Žåãã³ã¢ã¯çµæãæç€ºçãªæ¿èªè èªå¯ãšããŠæ±ããŸãã - ãã£ãã«ææã®ãã€ãã£ãã³ãŒã«ããã¯ãæ¿èªãçŽæ¥è§£æ±ºããå Žåã¯ã解決åã«
isImplicitSameChatApprovalAuthorization(...)ã䜿çšããŠãã ãããããã«ããã æé»ã®ãã©ãŒã«ããã¯ãåŒãç¶ããã£ãã«ã®éåžžã®ã¢ã¯ã¿ãŒèªå¯ãçµç±ããŸãã
ãã€ããŒãã®ã©ã€ããµã€ã¯ã«ãšã»ããã¢ããã¬ã€ãã³ã¹
- éè€ããããŒã«ã«æ¿èªããã³ãããé衚瀺ã«ããããŸãã¯é
ä¿¡åã«å
¥åäžã€ã³ãžã±ãŒã¿ãŒãéä¿¡ãããªã©ããã£ãã«åºæã®ãã€ããŒãã©ã€ããµã€ã¯ã«åäœã«ã¯ã
outbound.shouldSuppressLocalPayloadPromptãŸãã¯outbound.beforeDeliverPayloadã䜿çšããŸãã - ç¡å¹æã®å¿çã§ããã€ãã£ã exec æ¿èªãæå¹ã«ããããã«å¿
èŠãªæ£ç¢ºãªèšå®é
ç®ã説æããå Žåã¯ã
approvalCapability.describeExecApprovalSetupã䜿çšããŸãããã®ããã¯ã¯{ channel, channelLabel, accountId }ãåãåããŸãã ååä»ãã¢ã«ãŠã³ããæã€ãã£ãã«ã§ã¯ããããã¬ãã«ã®ããã©ã«ãã§ã¯ãªããchannels.<channel>.accounts.<id>.execApprovals.*ã®ãããªã¢ã«ãŠã³ãã¹ã³ãŒãã®ãã¹ã衚瀺ããå¿ èŠããããŸãã - Plugin æ¿èªã®ã«ãŒããªããšã©ãŒãã¿ã€ã ã¢ãŠããšã©ãŒã«å¯ŸããŠãPlugin æ¿èªå€±æã®ã¬ã€ãã³ã¹ãå®å
šã«è¡šç€ºã§ããå Žåã¯ã
approvalCapability.describePluginApprovalSetupã䜿çšããŸããcreateApproverRestrictedNativeApprovalCapability(...)ã¯ããããdescribeExecApprovalSetupããæšæž¬ããŸãããPlugin æ¿èªãš exec æ¿èªãå®éã«åããã€ãã£ãèšå®ã䜿çšããå Žåã«éããåããã«ããŒãæç€ºçã«æž¡ããŠãã ããã
ãã€ãã£ãæ¿èªã®é ä¿¡
ãã£ãã«ã§ãã€ãã£ãæ¿èªã®é
ä¿¡ãå¿
èŠãªå Žåããã£ãã«ã³ãŒãã¯ã¿ãŒã²ããã®æ£èŠåãšãã©ã³ã¹ããŒãïŒãã¬ãŒã³ããŒã·ã§ã³æ
å ±ã«éäžãããŸãã
openclaw/plugin-sdk/approval-runtime ã®
createChannelExecApprovalProfileãcreateChannelNativeOriginTargetResolverã
createChannelApproverDmTargetResolverãããã³
createApproverRestrictedNativeApprovalCapability ã䜿çšããŸãããã£ãã«åºæã®æ
å ±ã¯
approvalCapability.nativeRuntime ã®èåŸã«é
眮ããå¯èœã§ããã°
createChannelApprovalNativeRuntimeAdapter(...) ãŸãã¯
createLazyChannelApprovalNativeRuntimeAdapter(...) ã䜿çšããŸããããã«ãããã³ã¢ããã³ãã©ãŒãæ§æãããªã¯ãšã¹ãã®ãã£ã«ã¿ãªã³ã°ãã«ãŒãã£ã³ã°ãéè€æé€ãæå¹æéãGateway
ãµãã¹ã¯ãªãã·ã§ã³ãããã³å¥ã®å Žæãžã«ãŒãã£ã³ã°ãããããšã瀺ãéç¥ã管çã§ããŸãã
nativeRuntime ã¯ãããã€ãã®å°ããªã·ãŒã ã«åå²ãããŠããŸãã
availability- ã¢ã«ãŠã³ããèšå®ãããŠããããããã³ãªã¯ãšã¹ããåŠçãã¹ããpresentation- å ±ææ¿èªãã¥ãŒã¢ãã«ããä¿çäžïŒè§£æ±ºæžã¿ïŒæéåãã®ãã€ãã£ããã€ããŒããŸãã¯æçµã¢ã¯ã·ã§ã³ã«ãããã³ã°transport- ã¿ãŒã²ãããæºåãããã€ãã£ãæ¿èªã¡ãã»ãŒãžãéä¿¡ïŒæŽæ°ïŒåé€interactions- ãã€ãã£ããã¿ã³ãŸãã¯ãªã¢ã¯ã·ã§ã³åãã®ããªãã·ã§ã³ã®ãã€ã³ãïŒãã€ã³ãè§£é€ïŒã¢ã¯ã·ã§ã³ã¯ãªã¢ããã¯ãšããªãã·ã§ã³ã®cancelDeliveredããã¯ãdeliverPendingãããã»ã¹å ãŸãã¯æ°žç¶çãªç¶æ ïŒãªã¢ã¯ã·ã§ã³ã¿ãŒã²ããã¹ãã¢ãªã©ïŒãç»é²ããå Žåã¯ãcancelDeliveredãå®è£ ããŠãã ãããããã«ãããbindPendingãå®è¡ãããåã«ãã³ãã©ãŒã®åæ¢ã«ãã£ãŠé ä¿¡ããã£ã³ã»ã«ãããå ŽåããŸãã¯bindPendingããã³ãã«ãè¿ããªãå Žåã«ããã®ç¶æ ãè§£æŸã§ããŸãobserve- ãªãã·ã§ã³ã®é 信蚺æããã¯
ãã®ä»ã®æ¿èªãã«ããŒïŒ
- ãã£ãã«ãã»ãã·ã§ã³èµ·ç¹ã®ãã€ãã£ãé
ä¿¡ãšæç€ºçãªæ¿èªè»¢éå
ã®äž¡æ¹ããµããŒãããå Žåã¯ã
openclaw/plugin-sdk/approval-native-runtimeã®createNativeApprovalChannelRouteGatesã䜿çšããŸãããã®ãã«ããŒã¯ãæ¿èªèšå®ã®éžæãmodeã®åŠçããšãŒãžã§ã³ãïŒã»ãã·ã§ã³ãã£ã«ã¿ãŒãã¢ã«ãŠã³ãã®ãã€ã³ããã»ãã·ã§ã³ã¿ãŒã²ããã®ç §åãããã³ã¿ãŒã²ãããªã¹ãã®ç §åãäžå åããŸããäžæ¹ãåŒã³åºãåŽã¯åŒãç¶ãããã£ãã« IDãããã©ã«ãã®è»¢éã¢ãŒããã¢ã«ãŠã³ãæ€çŽ¢ããã©ã³ã¹ããŒãæå¹åãã§ãã¯ãã¿ãŒã²ããæ£èŠåãããã³ã¿ãŒã³ãœãŒã¹ã¿ãŒã²ããã®è§£æ±ºã管çããŸããã³ã¢ææã®ãã£ãã«ããªã·ãŒããã©ã«ããäœæããããã«äœ¿çšããªãã§ãã ããããã£ãã«ã§ææžåãããããã©ã«ãã¢ãŒããæç€ºçã«æž¡ããŸãã createChannelNativeOriginTargetResolverã¯ãããã©ã«ãã§{ to, accountId, threadId }ã¿ãŒã²ããã«å ±æãã£ãã«ã«ãŒããããã£ãŒã䜿çšããŸããSlack ã®ã¿ã€ã ã¹ã¿ã³ããã¬ãã£ãã¯ã¹ç §åãªã©ããã£ãã«ã«ãããã€ããŒåºæã®åå€èŠåãããå Žåã«éããtargetsMatchãæž¡ããŸããããã©ã«ãã®ã«ãŒããããã£ãŒãŸãã¯ã«ã¹ã¿ã ã®targetsMatchã³ãŒã«ããã¯ãå®è¡ãããåã«ããã£ãã«ã§ãããã€ã㌠ID ãæ£èŠåããå¿ èŠãããããã€é ä¿¡çšã«ã¯å ã®ã¿ãŒã²ãããä¿æããå Žåã¯ãnormalizeTargetForMatchãæž¡ããŸãã解決ãããé ä¿¡ã¿ãŒã²ããèªäœãæ£èŠåããå¿ èŠãããå Žåã«éããnormalizeTargetã䜿çšããŸãã- ãã£ãã«ã§ã¯ã©ã€ã¢ã³ããããŒã¯ã³ãBolt ã¢ããªãWebhook ã¬ã·ãŒããŒãªã©ã®ã©ã³ã¿ã€ã ææãªããžã§ã¯ããå¿
èŠãªå Žåã¯ã
openclaw/plugin-sdk/channel-runtime-contextãä»ããŠç»é²ããŸããæ±çšã©ã³ã¿ã€ã ã³ã³ããã¹ãã¬ãžã¹ããªã«ãããæ¿èªåºæã®ã©ãããŒæ¥çã³ãŒãã远å ããã«ãã³ã¢ããã£ãã«ã®èµ·åç¶æ ããã±ã€ãããªãã£é§åã®ãã³ãã©ãŒãããŒãã¹ãã©ããã§ããŸãã - ã±ã€ãããªãã£é§åã®ã·ãŒã ã§ã¯ãŸã ååã«è¡šçŸã§ããªãå Žåã«éããäœã¬ãã«ã®
createChannelApprovalHandlerãŸãã¯createChannelNativeApprovalRuntimeã䜿çšããŸãã - ãã€ãã£ãæ¿èªãã£ãã«ã¯ããããã®ãã«ããŒãéããŠ
accountIdãšapprovalKindã®äž¡æ¹ãã«ãŒãã£ã³ã°ããå¿ èŠããããŸããaccountIdã«ãããè€æ°ã¢ã«ãŠã³ãã®æ¿èªããªã·ãŒãé©åãªãããã¢ã«ãŠã³ãã«ã¹ã³ãŒãã§ããŸãããŸããapprovalKindã«ãããã³ã¢ã«ããŒãã³ãŒããããåå²ãèšããã«ãexec æ¿èªãš Plugin æ¿èªã®åäœããã£ãã«ã§å©çšã§ããŸãã - æ¿èªã®åã«ãŒãã£ã³ã°éç¥ãã³ã¢ã管çããŸãããã£ãã« Plugin ã¯
createChannelNativeApprovalRuntimeããç¬èªã®ãæ¿èªã¯ DMïŒå¥ã®ãã£ãã«ã«éä¿¡ãããŸããããšãããã©ããŒã¢ããã¡ãã»ãŒãžãéä¿¡ããŠã¯ãããŸããã代ããã«ãå ±ææ¿èªã±ã€ãããªãã£ãã«ããŒãéããŠæ£ç¢ºãªèµ·ç¹ïŒæ¿èªè DM ã«ãŒãã£ã³ã°ãå ¬éããéå§å ã®ãã£ããã«éç¥ãæçš¿ããåã«ãã³ã¢ãå®éã®é ä¿¡ãéçŽã§ããããã«ããŸãã - é ä¿¡ãããæ¿èª ID ã®çš®é¡ããšã³ãããŒãšã³ãã§ç¶æããŸãããã€ãã£ãã¯ã©ã€ã¢ã³ãã¯ããã£ãã«ããŒã«ã«ã®ç¶æ ãã exec æ¿èªãš Plugin æ¿èªã®ã«ãŒãã£ã³ã°ãæšæž¬ãŸãã¯æžãæããŠã¯ãããŸããã
- ãã®æç€ºçãª
approvalKindãresolveApprovalOverGatewayã«æž¡ããŸããããã¯æ£èŠã®approval.resolveãµãŒãã¹ã䜿çšããå¥ã®ãµãŒãã§ã¹ãå ã«å¿çããå Žåã¯ãèšé²ãããåè ãè¿ããŸãã以åã®æç€ºçãªresolveMethodå ¥åã¯ãã³ãã³ãããŒã¹ã®ã³ã³ãããŒã«åãã«åŒãç¶ãååšããŸããæ°ãããã€ãã£ãã¢ã¯ã·ã§ã³ã§ã¯ãããã䜿çšããããID ããçš®é¡ãæšæž¬ãããããŠã¯ãããŸããã - æ¿èªã®çš®é¡ããšã«ãæå³çã«ç°ãªããã€ãã£ããµãŒãã§ã¹ãå ¬éã§ããŸããçŸåšãã³ãã«ãããŠããäŸïŒMatrix ã¯ãexec æ¿èªãš Plugin æ¿èªã§åããã€ãã£ã DMïŒãã£ãã«ã«ãŒãã£ã³ã°ãšãªã¢ã¯ã·ã§ã³ UX ãç¶æãã€ã€ãèªèšŒã¯æ¿èªã®çš®é¡ã«ãã£ãŠç°ãªãããã«ããŠããŸããSlack ã¯ãexec ID ãš Plugin ID ã®äž¡æ¹ã§ãã€ãã£ãæ¿èªã«ãŒãã£ã³ã°ãå©çšå¯èœã«ããŠããŸãã
createApproverRestrictedNativeApprovalAdapterã¯äºææ§ã©ãããŒãšããŠåŒãç¶ãååšããŸãããæ°ããã³ãŒãã§ã¯ã±ã€ãããªãã£ãã«ããŒãåªå ããPlugin äžã§approvalCapabilityãå ¬éããå¿ èŠããããŸãã
ããéå®çãªæ¿èªã©ã³ã¿ã€ã ã®ãµããã¹
é »ç¹ã«å®è¡ããããã£ãã«ãšã³ããªãã€ã³ãã§ã¯ããã®ãã¡ããªãŒã®äžéšã ããå¿
èŠãªå Žåãåºç¯ãª
approval-runtime ãã¬ã«ããããæ¬¡ã®éå®çãªãµããã¹ãåªå
ããŠãã ããã
openclaw/plugin-sdk/approval-auth-runtimeopenclaw/plugin-sdk/approval-client-runtimeopenclaw/plugin-sdk/approval-delivery-runtimeopenclaw/plugin-sdk/approval-gateway-runtimeopenclaw/plugin-sdk/approval-reference-runtimeopenclaw/plugin-sdk/approval-handler-adapter-runtimeopenclaw/plugin-sdk/approval-handler-runtimeopenclaw/plugin-sdk/approval-native-runtimeopenclaw/plugin-sdk/approval-reply-runtimeopenclaw/plugin-sdk/channel-runtime-context
åæ§ã«ããã¹ãŠãå¿
èŠã§ãªãå Žåã¯ãããåºç¯ãªå
æ¬ãµãŒãã§ã¹ããã
openclaw/plugin-sdk/reply-runtimeã
openclaw/plugin-sdk/reply-dispatch-runtimeã
openclaw/plugin-sdk/reply-referenceãããã³
openclaw/plugin-sdk/reply-chunking ãåªå
ããŠãã ããã
ã»ããã¢ããã®ãµããã¹
openclaw/plugin-sdk/setup-runtimeã¯ãã©ã³ã¿ã€ã ã§å®å šãªã»ããã¢ãããã«ããŒã察象ãšããŸãïŒcreateSetupTranslatorãã€ã³ããŒãæã«å®å šãªã»ããã¢ãããããã¢ããã¿ãŒ ïŒcreatePatchedAccountSetupAdapterãcreateEnvPatchedAccountSetupAdapterãcreateSetupInputPresenceValidatorïŒãæ€çŽ¢æ³šèšã®åºåãpromptResolvedAllowFromãsplitSetupEntriesãããã³å§ä»»ã»ããã¢ãããããã·ãã«ããŒãopenclaw/plugin-sdk/channel-setupã¯ããªãã·ã§ã³ã€ã³ã¹ããŒã«ã®ã»ããã¢ãããã«ããŒãšãã»ããã¢ããæã«å®å šãªããã€ãã®ããªããã£ãã察象ãšããŸãïŒcreateOptionalChannelSetupSurfaceãcreateOptionalChannelSetupAdapterãcreateOptionalChannelSetupWizardãDEFAULT_ACCOUNT_IDãcreateTopLevelChannelDmPolicyãsetSetupChannelEnabledãããã³splitSetupEntriesãmoveSingleAccountChannelSectionToDefaultAccount(...)ãªã©ãããéãå ±æã»ããã¢ããïŒèšå®ãã«ããŒãå¿ èŠãªå Žåã«éããããåºç¯ãªopenclaw/plugin-sdk/setupã·ãŒã ã䜿çšããŸãã
ãã£ãã«ãã»ããã¢ãããµãŒãã§ã¹ã§ããŸããã® Plugin ãã€ã³ã¹ããŒã«ããŠãã ããããšæ¡å
ããã ãã®å Žåã¯ãcreateOptionalChannelSetupSurface(...) ãåªå
ããŠãã ãããçæãããã¢ããã¿ãŒïŒãŠã£ã¶ãŒãã¯ãèšå®ã®æžã蟌ã¿ãšæçµåŠçã§ãã§ã€ã«ã¯ããŒãºããæ€èšŒãæçµåŠçãããã³ããã¥ã¡ã³ããªã³ã¯ã®ã³ããŒã§åãã€ã³ã¹ããŒã«å¿
é ã¡ãã»ãŒãžãåå©çšããŸãã
ãã£ãã«ãç°å¢å€æ°é§åã®ã»ããã¢ãããŸãã¯èªèšŒããµããŒãããæ±çšã®èµ·åïŒèšå®ãããŒãã©ã³ã¿ã€ã ã®ããŒãåã«ãããã®ç°å¢å€æ°åãææ¡ããå¿
èŠãããå Žåã¯ãPlugin ãããã§ã¹ãã® channelEnvVars ã§å®£èšããŸãããã£ãã«ã©ã³ã¿ã€ã ã® envVars ãŸãã¯ããŒã«ã«å®æ°ã¯ããªãã¬ãŒã¿ãŒåãã®æé¢å°çšãšããŠç¶æããŸãã
Plugin ã©ã³ã¿ã€ã ã®èµ·ååã«ãã£ãã«ã statusãchannels listãchannels statusããŸãã¯
SecretRef ã¹ãã£ã³ã«è¡šç€ºãããå¯èœæ§ãããå Žåã¯ãpackage.json ã« openclaw.setupEntry ã远å ããŸãããã®ãšã³ããªãã€ã³ãã¯èªã¿åãå°çšã³ãã³ããã¹ã§å®å
šã«ã€ã³ããŒãã§ãããããã®æŠèŠã«å¿
èŠãªãã£ãã«ã¡ã¿ããŒã¿ãã»ããã¢ããæã«å®å
šãªèšå®ã¢ããã¿ãŒãã¹ããŒã¿ã¹ã¢ããã¿ãŒãããã³ãã£ãã«ã·ãŒã¯ã¬ããã¿ãŒã²ããã¡ã¿ããŒã¿ãè¿ãå¿
èŠããããŸããã»ããã¢ãããšã³ããªããã¯ã©ã€ã¢ã³ãããªã¹ããŒããŸãã¯ãã©ã³ã¹ããŒãã©ã³ã¿ã€ã ãèµ·åããŠã¯ãããŸããã
ã¡ã€ã³ãã£ãã«ãšã³ããªã®ã€ã³ããŒããã¹ãéå®çã«ä¿ã¡ãŸãããã£ã¹ã«ããªãŒã¯ããã£ãã«ãæå¹åããã«ãšã³ããªãšãã£ãã« Plugin ã¢ãžã¥ãŒã«ãè©äŸ¡ããŠãã±ã€ãããªãã£ãç»é²ã§ããŸãã
channel-plugin-api.ts ã®ãããªãã¡ã€ã«ã§ã¯ãã»ããã¢ãããŠã£ã¶ãŒãããã©ã³ã¹ããŒãã¯ã©ã€ã¢ã³ãããœã±ãããªã¹ããŒããµãããã»ã¹ã©ã³ãã£ãŒããŸãã¯ãµãŒãã¹èµ·åã¢ãžã¥ãŒã«ãã€ã³ããŒãããã«ããã£ãã« Plugin ãªããžã§ã¯ãããšã¯ã¹ããŒãããå¿
èŠããããŸãããããã®ã©ã³ã¿ã€ã èŠçŽ ã¯ãregisterFull(...)ãã©ã³ã¿ã€ã ã»ãã¿ãŒããŸãã¯é
å»¶ã±ã€ãããªãã£ã¢ããã¿ãŒããããŒããããã¢ãžã¥ãŒã«ã«é
眮ããŸãã
ãã®ä»ã®éå®çãªãã£ãã«ãµããã¹
ãã®ä»ã®é »ç¹ã«å®è¡ããããã£ãã«ãã¹ã§ã¯ãããåºç¯ãªã¬ã¬ã·ãŒãµãŒãã§ã¹ãããéå®çãªãã«ããŒãåªå ããŠãã ããã
- è€æ°ã¢ã«ãŠã³ãèšå®ãšããã©ã«ãã¢ã«ãŠã³ããžã®ãã©ãŒã«ããã¯ã«ã¯ã
openclaw/plugin-sdk/account-coreãopenclaw/plugin-sdk/account-idãopenclaw/plugin-sdk/account-resolutionãããã³openclaw/plugin-sdk/account-helpers - åä¿¡ã«ãŒãïŒãšã³ãããŒãããã³èšé²ïŒãã£ã¹ãããã®é
ç·ã«ã¯ã
openclaw/plugin-sdk/inbound-envelopeããã³openclaw/plugin-sdk/channel-inbound - ã¿ãŒã²ããè§£æãã«ããŒã«ã¯ã
openclaw/plugin-sdk/channel-targets - ã¡ãã£ã¢ã®èªã¿èŸŒã¿ã«ã¯
openclaw/plugin-sdk/outbound-mediaãéä¿¡ IDïŒéä¿¡ããªã²ãŒãããã³ãã€ããŒãèšç»ã«ã¯openclaw/plugin-sdk/channel-outbound - éä¿¡ã«ãŒãã§æç€ºçãª
replyToIdïŒthreadIdãç¶æããå¿ èŠãããå ŽåããŸãã¯ããŒã¹ã»ãã·ã§ã³ããŒãåŒãç¶ãäžèŽããŠããç¶æ ã§çŸåšã®:thread:ã»ãã·ã§ã³ã埩å ããå¿ èŠãããå Žåã¯ãopenclaw/plugin-sdk/channel-coreã®buildThreadAwareOutboundSessionRoute(...)ããã©ãããã©ãŒã ã«ãã€ãã£ããªã¹ã¬ããé ä¿¡ã»ãã³ãã£ã¯ã¹ãããå Žåããããã€ã㌠Plugin ã¯åªå é äœããµãã£ãã¯ã¹åäœãããã³ã¹ã¬ãã ID ã®æ£èŠåããªãŒããŒã©ã€ãã§ããŸãã - ã¹ã¬ãããã€ã³ãã£ã³ã°ã®ã©ã€ããµã€ã¯ã«ãšã¢ããã¿ãŒç»é²ã«ã¯ã
openclaw/plugin-sdk/thread-bindings-runtime - ã¬ã¬ã·ãŒãªãšãŒãžã§ã³ãïŒã¡ãã£ã¢ãã€ããŒããã£ãŒã«ãã®ã¬ã€ã¢ãŠããåŒãç¶ãå¿
èŠãªå Žåã«éãã
openclaw/plugin-sdk/agent-media-payload - Telegram ã®ã«ã¹ã¿ã ã³ãã³ãæ£èŠåãéè€ïŒç«¶åæ€èšŒãããã³ãã©ãŒã«ããã¯æãå®å®ããã³ãã³ãèšå®å¥çŽã«ã¯ã
openclaw/plugin-sdk/telegram-command-configïŒéæšå¥šïŒæ¬çªç°å¢ã§äœ¿çšãããã³ãã«æžã¿ Plugin ã¯ãããŸããïŒãæ°ãã Plugin ã³ãŒãã§ã¯ãPlugin ããŒã«ã«ã®ã³ãã³ãèšå®åŠçãåªå ããŠãã ãã
èªèšŒå°çšãã£ãã«ã¯ãéåžžã¯ããã©ã«ããã¹ãŸã§ã§ååã§ããã³ã¢ãæ¿èªãåŠçããPlugin ã¯éä¿¡ïŒèªèšŒã±ã€ãããªãã£ã®ã¿ãå ¬éããŸããMatrixãSlackãTelegramãããã³ã«ã¹ã¿ã ãã£ãããã©ã³ã¹ããŒãã®ãããªãã€ãã£ãæ¿èªãã£ãã«ã§ã¯ãç¬èªã®æ¿èªã©ã€ããµã€ã¯ã«ãå®è£ ãã代ããã«ãå ±æãã€ãã£ããã«ããŒã䜿çšããå¿ èŠããããŸãã
åä¿¡ã¡ã³ã·ã§ã³ããªã·ãŒ
åä¿¡ã¡ã³ã·ã§ã³åŠçã¯ã次㮠2 å±€ã«åããŠç¶æããŸãã
- Plugin ææã®èšŒæ åé
- å ±æããªã·ãŒè©äŸ¡
ã¡ã³ã·ã§ã³ããªã·ãŒã®å€å®ã«ã¯ openclaw/plugin-sdk/channel-mention-gating ã䜿çšããŸããããåºç¯ãªåä¿¡ãã«ããŒãã¬ã«ãå¿
èŠãªå Žåã«éããopenclaw/plugin-sdk/channel-inbound ã䜿çšããŸãã
Plugin ããŒã«ã«ããžãã¯ã«é©ãããã®ïŒ
- ããããžã®è¿ä¿¡ã®æ€åº
- åŒçšããããããã®æ€åº
- ã¹ã¬ããåå ã®ãã§ãã¯
- ãµãŒãã¹ïŒã·ã¹ãã ã¡ãã»ãŒãžã®é€å€
- ãããã®åå ã蚌æããããã«å¿ èŠãªãã©ãããã©ãŒã ãã€ãã£ãã®ãã£ãã·ã¥
å ±æãã«ããŒã«é©ãããã®ïŒ
requireMention- æç€ºçãªã¡ã³ã·ã§ã³çµæ
- æé»çãªã¡ã³ã·ã§ã³èš±å¯ãªã¹ã
- ã³ãã³ãã«ãããã€ãã¹
- æçµçãªã¹ãããå€å®
æšå¥šãããŒïŒ
- ããŒã«ã«ã®ã¡ã³ã·ã§ã³æ å ±ãèšç®ããŸãã
- ãã®æ
å ±ã
resolveInboundMentionDecision({ facts, policy })ã«æž¡ããŸãã - åä¿¡ã²ãŒãã§
decision.effectiveWasMentionedãdecision.shouldBypassMentionãdecision.shouldSkipã䜿çšããŸãã
implicitMentionKindWhen, matchesMentionWithExplicit, resolveInboundMentionDecision,} from "openclaw/plugin-sdk/channel-inbound"; const wasMentioned = matchesMentionWithExplicit({ text, mentionRegexes, explicit: { hasAnyMention, isExplicitlyMentioned, canResolveExplicit, },}); const facts = { canDetectMention: true, wasMentioned, hasAnyMention, implicitMentionKinds: [ ...implicitMentionKindWhen("reply_to_bot", isReplyToBot), ...implicitMentionKindWhen("quoted_bot", isQuoteOfBot), ],}; const decision = resolveInboundMentionDecision({ facts, policy: { isGroup, requireMention, allowedImplicitMentionKinds: requireExplicitMention ? [] : ["reply_to_bot", "quoted_bot"], allowTextCommands, hasControlCommand, commandAuthorized, },}); if (decision.shouldSkip) return;matchesMentionWithExplicit(...) ã¯ããŒã«å€ãè¿ããŸããhasAnyMentionã
isExplicitlyMentionedãcanResolveExplicit ã¯ããã£ã³ãã«ç¬èªã®
ãã€ãã£ããªã¡ã³ã·ã§ã³ã¡ã¿ããŒã¿ïŒã¡ãã»ãŒãžãšã³ãã£ãã£ãããããžã®è¿ä¿¡ãã©ã°ãªã©ïŒããååŸããŸãã
ãã©ãããã©ãŒã ã§æ€åºã§ããªãå Žåã¯ãfalse/undefined å€ãæå®ããŠãã ããã
api.runtime.channel.mentions ã¯ããã§ã«ã©ã³ã¿ã€ã 泚å
¥ã«äŸåããŠãã
ãã³ãã«æžã¿ãã£ã³ãã«ãã©ã°ã€ã³åãã«ãåãå
±æã¡ã³ã·ã§ã³ãã«ããŒãå
¬éããŸãïŒ
buildMentionRegexesãmatchesMentionPatternsãmatchesMentionWithExplicitã
implicitMentionKindWhenãresolveInboundMentionDecisionã
implicitMentionKindWhen ãš resolveInboundMentionDecision ã ããå¿
èŠãªå Žåã¯ã
ç¡é¢ä¿ãªåä¿¡ã©ã³ã¿ã€ã ãã«ããŒã®èªã¿èŸŒã¿ãé¿ããããã
openclaw/plugin-sdk/channel-mention-gating ããã€ã³ããŒãããŠãã ããã
æé
ããã±ãŒãžãšãããã§ã¹ã
æšæºã®ãã©ã°ã€ã³ãã¡ã€ã«ãäœæããŸãã
openclaw.plugin.json ã® channels ãã£ãŒã«ãïŒkind ãã£ãŒã«ãã§ã¯ãããŸããïŒãã
ãããã§ã¹ãããã£ã³ãã«ãææããããšã瀺ããŸããããã±ãŒãžã¡ã¿ããŒã¿ã®å
šé
ç®ã«ã€ããŠã¯ã
ãã©ã°ã€ã³ã®ã»ããã¢ãããšèšå®ãåç
§ããŠãã ããïŒ
{"name": "@myorg/openclaw-acme-chat","version": "1.0.0","type": "module","openclaw": { "extensions": ["./index.ts"], "setupEntry": "./setup-entry.ts", "channel": { "id": "acme-chat", "label": "Acme Chat", "blurb": "OpenClaw ã Acme Chat ã«æ¥ç¶ããŸãã" }}}{"id": "acme-chat","channels": ["acme-chat"],"name": "Acme Chat","description": "Acme Chat ãã£ã³ãã«ãã©ã°ã€ã³","configSchema": { "type": "object", "additionalProperties": false, "properties": {}},"channelConfigs": { "acme-chat": { "schema": { "type": "object", "additionalProperties": false, "properties": { "token": { "type": "string" }, "allowFrom": { "type": "array", "items": { "type": "string" } } } }, "uiHints": { "token": { "label": "ãããããŒã¯ã³", "sensitive": true } } }}}configSchema 㯠plugins.entries.acme-chat.config ãæ€èšŒããŸãããã£ã³ãã«ã¢ã«ãŠã³ãèšå®ã§ã¯ãªãã
ãã©ã°ã€ã³ãææããèšå®ã«äœ¿çšããŠãã ããã
channelConfigs.acme-chat.schema 㯠channels.acme-chat ãæ€èšŒãã
ãã©ã°ã€ã³ã©ã³ã¿ã€ã ãèªã¿èŸŒãŸããåã«ãèšå®ã¹ããŒããã»ããã¢ãããUI ãµãŒãã§ã¹ã§äœ¿çšããã
ã³ãŒã«ããã¹ã®ãœãŒã¹ã§ãããããã¬ãã«ãã£ãŒã«ãã®å®å
šãªãªãã¡ã¬ã³ã¹ã«ã€ããŠã¯ã
ãã©ã°ã€ã³ãããã§ã¹ããåç
§ããŠãã ããã
ãã£ã³ãã«ãã©ã°ã€ã³ãªããžã§ã¯ããæ§ç¯ãã
ChannelPlugin ã€ã³ã¿ãŒãã§ãŒã¹ã«ã¯ãå€ãã®ãªãã·ã§ã³ã®ã¢ããã¿ãŒãµãŒãã§ã¹ããããŸãã
ãŸãæå°æ§æã® idãconfigãsetup ããå§ããå¿
èŠã«å¿ããŠ
ã¢ããã¿ãŒã远å ããŠãã ããã
src/channel.ts ãäœæããŸãïŒ
import { createChatChannelPlugin, createChannelPluginBase,} from "openclaw/plugin-sdk/channel-core";import type { OpenClawConfig } from "openclaw/plugin-sdk/channel-core";import { acmeChatApi } from "./client.js"; // ãã©ãããã©ãŒã ã® API ã¯ã©ã€ã¢ã³ã type ResolvedAccount = { accountId: string | null; token: string; allowFrom: string[]; dmPolicy: string | undefined;}; function resolveAccount( cfg: OpenClawConfig, accountId?: string | null,): ResolvedAccount { const section = (cfg.channels as Record<string, any>)?.["acme-chat"]; const token = section?.token; if (!token) throw new Error("acme-chat: ããŒã¯ã³ãå¿
èŠã§ã"); return { accountId: accountId ?? null, token, allowFrom: section?.allowFrom ?? [], dmPolicy: section?.dmSecurity, };} export const acmeChatPlugin = createChatChannelPlugin<ResolvedAccount>({ base: createChannelPluginBase({ id: "acme-chat", // ã¢ã«ãŠã³ãã®è§£æ±ºã𿀿»ã¯ `setup` ã§ã¯ãªã `config` ã«å±ããŸãã // `setup` ã¯ãªã³ããŒãã£ã³ã°æã®æžã蟌ã¿ïŒapplyAccountConfigãvalidateInputïŒãæ±ããŸãã config: { listAccountIds: () => ["default"], resolveAccount, inspectAccount(cfg, accountId) { const section = (cfg.channels as Record<string, any>)?.["acme-chat"]; return { enabled: Boolean(section?.token), configured: Boolean(section?.token), tokenStatus: section?.token ? "available" : "missing", }; }, }, setup: { applyAccountConfig: ({ cfg, input }) => ({ ...cfg, channels: { ...cfg.channels, "acme-chat": { ...(cfg.channels as any)?.["acme-chat"], ...input }, }, }), }, }), // DM ã»ãã¥ãªãã£ïŒãããã«ã¡ãã»ãŒãžãéä¿¡ã§ãããŠãŒã¶ãŒ security: { dm: { channelKey: "acme-chat", resolvePolicy: (account) => account.dmPolicy, resolveAllowFrom: (account) => account.allowFrom, defaultPolicy: "allowlist", }, }, // ãã¢ãªã³ã°ïŒæ°ãã DM é£çµ¡å
ã®æ¿èªãã㌠pairing: { text: { idLabel: "Acme Chat ãŠãŒã¶ãŒå", message: "æ¬äººç¢ºèªã®ããããã®ã³ãŒããéä¿¡ããŠãã ããïŒ", notify: async ({ target, code }) => { await acmeChatApi.sendDm(target, `ãã¢ãªã³ã°ã³ãŒãïŒ${code}`); }, }, }, // ã¹ã¬ããåŠçïŒè¿ä¿¡ã®é
ä¿¡æ¹æ³ threading: { topLevelReplyToMode: "reply" }, // éä¿¡ïŒãã©ãããã©ãŒã ã«ã¡ãã»ãŒãžãéä¿¡ outbound: { attachedResults: { channel: "acme-chat", sendText: async (params) => { const result = await acmeChatApi.sendMessage( params.to, params.text, ); return { messageId: result.id }; }, }, base: { sendMedia: async (params) => { await acmeChatApi.sendFile(params.to, params.filePath); }, }, },});æ£èŠã®ãããã¬ãã« DM ããŒãšåŸæ¥ã®ãã¹ããããããŒã®äž¡æ¹ãåãå
¥ãããã£ã³ãã«ã§ã¯ãplugin-sdk/channel-config-helpers ã®ãã«ããŒã䜿çšããŠãã ãããresolveChannelDmAccessãresolveChannelDmPolicyãresolveChannelDmAllowFromãnormalizeChannelDmPolicy ã¯ãç¶æ¿ãããã«ãŒãå€ãããã¢ã«ãŠã³ãããŒã«ã«ã®å€ãåªå
ããŸããåããªãŸã«ããŒã normalizeLegacyDmAliases ã«ãã doctor 修埩ãšçµã¿åãããã©ã³ã¿ã€ã ãšç§»è¡ãåãå¥çŽãèªã¿åãããã«ããŠãã ããã
createChatChannelPlugin ãèªåã§è¡ãããš
äœã¬ãã«ã®ã¢ããã¿ãŒã€ã³ã¿ãŒãã§ãŒã¹ãæåã§å®è£ ãã代ããã«ã 宣èšçãªãªãã·ã§ã³ãæž¡ããšããã«ããŒãããããæ§æããŸãïŒ
| ãªãã·ã§ã³ | æ¥ç¶ãããæ©èœ |
|---|---|
security.dm |
èšå®ãã£ãŒã«ãããã®ã¹ã³ãŒãä»ã DM ã»ãã¥ãªãã£ãªãŸã«ã㌠|
pairing.text |
ã³ãŒã亀æã䜿çšããããã¹ãããŒã¹ã® DM ãã¢ãªã³ã°ãã㌠|
threading |
è¿ä¿¡å ã¢ãŒãã®ãªãŸã«ããŒïŒåºå®ãã¢ã«ãŠã³ãã¹ã³ãŒãããŸãã¯ã«ã¹ã¿ã ïŒ |
outbound.attachedResults |
çµæã¡ã¿ããŒã¿ïŒã¡ãã»ãŒãž IDïŒãè¿ãéä¿¡é¢æ°ãã³ã¢ãè¿ãããé
ä¿¡çµæã«è奿
å ±ãä»äžã§ãããããåé局㫠channel ID ãå¿
èŠ |
å®å šãªå¶åŸ¡ãå¿ èŠãªå Žåã¯ã宣èšçãªãªãã·ã§ã³ã®ä»£ããã« çã®ã¢ããã¿ãŒãªããžã§ã¯ããæž¡ãããšãã§ããŸãã
çã®éä¿¡ã¢ããã¿ãŒã§ã¯ãchunker(text, limit, ctx) 颿°ãå®çŸ©ã§ããŸãã
ãªãã·ã§ã³ã® ctx.formatting ã«ã¯ãmaxLinesPerMessage ãªã©ã
é
ä¿¡æã®æžåŒèšå®ã®æ±ºå®ãå«ãŸããŸããè¿ä¿¡ã®ã¹ã¬ããåŠçãšãã£ã³ã¯å¢çã
å
±æéä¿¡é
ä¿¡ã«ãã£ãŠäžåºŠã ã解決ããããããéä¿¡åã«é©çšããŠãã ããã
ãã€ãã£ããªè¿ä¿¡å
ã解決ãããå Žåãéä¿¡ã³ã³ããã¹ãã«ã¯
replyToIdSourceïŒimplicit ãŸã㯠explicitïŒãå«ãŸããŸããããã«ããã
ãã€ããŒããã«ããŒã¯æé»çãªåçºè¿ä¿¡ã¹ããããæ¶è²»ããã«ã
æç€ºçãªè¿ä¿¡ã¿ã°ãä¿æã§ããŸãã
ãšã³ããªãŒãã€ã³ããæ¥ç¶ãã
index.ts ãäœæããŸãïŒ
import { defineChannelPluginEntry } from "openclaw/plugin-sdk/channel-core";import { acmeChatPlugin } from "./src/channel.js"; export default defineChannelPluginEntry({ id: "acme-chat", name: "Acme Chat", description: "Acme Chat ãã£ã³ãã«ãã©ã°ã€ã³", plugin: acmeChatPlugin, registerCliMetadata(api) { api.registerCli( ({ program }) => { program .command("acme-chat") .description("Acme Chat ã®ç®¡ç"); }, { descriptors: [ { name: "acme-chat", description: "Acme Chat ã®ç®¡ç", hasSubcommands: false, }, ], }, ); }, registerFull(api) { api.registerGatewayMethod(/* ... */); },});ãã£ã³ãã«ãææãã CLI èšè¿°å㯠registerCliMetadata(...) ã«é
眮ããŠãã ãããããã«ãã OpenClaw ã¯ã
ãã£ã³ãã«ã©ã³ã¿ã€ã å
šäœãæå¹åããã«ã«ãŒããã«ããžè¡šç€ºã§ãã
éåžžã®å®å
šèªã¿èŸŒã¿ã§ãå®éã®ã³ãã³ãç»é²ã«åãèšè¿°åã䜿çšã§ããŸãã
ã©ã³ã¿ã€ã å°çšã®åŠç㯠registerFull(...) ã«æ®ããŠãã ããã
defineChannelPluginEntry ã¯ç»é²ã¢ãŒãã®åå²ãèªåçã«åŠçããŸãã
registerFull(...) ã§ Gateway RPC ã¡ãœãããç»é²ããå Žåã¯ã
ãã©ã°ã€ã³åºæã®ãã¬ãã£ãã¯ã¹ã䜿çšããŠãã ãããã³ã¢ç®¡çåå空éïŒconfig.*ã
exec.approvals.*ãwizard.*ãupdate.*ïŒã¯äºçŽããããŸãŸã§ãåžžã«
operator.admin ã«è§£æ±ºãããŸãããã¹ãŠã®ãªãã·ã§ã³ã«ã€ããŠã¯ã
ãšã³ããªãŒãã€ã³ããåç
§ããŠãã ããã
ã»ããã¢ãããšã³ããªãŒã远å ãã
ãªã³ããŒãã£ã³ã°äžã®è»œéèªã¿èŸŒã¿çšã« setup-entry.ts ãäœæããŸãïŒ
import { defineSetupPluginEntry } from "openclaw/plugin-sdk/channel-core";import { acmeChatPlugin } from "./src/channel.js"; export default defineSetupPluginEntry(acmeChatPlugin);ãã£ã³ãã«ãç¡å¹ãŸãã¯æªèšå®ã®å ŽåãOpenClaw ã¯å®å šãªãšã³ããªãŒã®ä»£ããã« ãããèªã¿èŸŒã¿ãŸããããã«ãããã»ããã¢ãããããŒäžã«éãã©ã³ã¿ã€ã ã³ãŒãã èªã¿èŸŒãŸãã«æžã¿ãŸãã詳现ã«ã€ããŠã¯ã ã»ããã¢ãããšèšå®ãåç §ããŠãã ããã
ã»ããã¢ããã§å®å
šã«äœ¿çšã§ãããšã¯ã¹ããŒãããµã€ãã«ãŒã¢ãžã¥ãŒã«ãžåå²ããŠãã
ãã³ãã«æžã¿ã¯ãŒã¯ã¹ããŒã¹ãã£ã³ãã«ã§ã¯ãã»ããã¢ããæã®æç€ºçãªã©ã³ã¿ã€ã ã»ãã¿ãŒã
å¿
èŠãªå Žåãopenclaw/plugin-sdk/channel-entry-contract ã®
defineBundledChannelSetupEntry(...) ã䜿çšã§ããŸãã
åä¿¡ã¡ãã»ãŒãžãåŠçãã
ãã©ã°ã€ã³ã¯ããã©ãããã©ãŒã ããã¡ãã»ãŒãžãåä¿¡ã㊠OpenClaw ã«è»¢éããå¿ èŠããããŸãã äžè¬çãªãã¿ãŒã³ã¯ããªã¯ãšã¹ããæ€èšŒãããã£ã³ãã«ã®åä¿¡ãã³ãã©ãŒãéã㊠ãã£ã¹ããããã Webhook ã§ãïŒ
registerFull(api) { api.registerHttpRoute({ path: "/acme-chat/webhook", auth: "plugin", // Plugin ã管çããèªèšŒïŒçœ²åã¯èªåã§æ€èšŒïŒ handler: async (req, res) => { const event = parseWebhookPayload(req); // åä¿¡ãã³ãã©ãŒã¯ã¡ãã»ãŒãžã OpenClaw ã«ãã£ã¹ãããããŸãã // æ£ç¢ºãªæ¥ç¶æ¹æ³ã¯ãã©ãããã©ãŒã SDK ã«ãã£ãŠç°ãªããŸãã // ãã³ãã«ãããŠãã Microsoft Teams ãŸã㯠Google Chat ã® Plugin ããã±ãŒãžã«ããå®äŸãåç
§ããŠãã ããã await handleAcmeChatInbound(api, event); res.statusCode = 200; res.end("ok"); return true; }, });}ãã¹ã
src/channel.test.ts ã«åãå Žæã«é
眮ãããã¹ããèšè¿°ããŸãã
import { describe, it, expect } from "vitest";import { acmeChatPlugin } from "./channel.js"; describe("acme-chat plugin", () => { it("resolves account from config", () => { const cfg = { channels: { "acme-chat": { token: "test-token", allowFrom: ["user1"] }, }, } as any; const account = acmeChatPlugin.config.resolveAccount(cfg, undefined); expect(account.token).toBe("test-token"); }); it("inspects account without materializing secrets", () => { const cfg = { channels: { "acme-chat": { token: "test-token" } }, } as any; const result = acmeChatPlugin.config.inspectAccount!(cfg, undefined); expect(result.configured).toBe(true); expect(result.tokenStatus).toBe("available"); }); it("reports missing config", () => { const cfg = { channels: {} } as any; const result = acmeChatPlugin.config.inspectAccount!(cfg, undefined); expect(result.configured).toBe(false); });});pnpm test <bundled-plugin-root>/acme-chat/å ±æãã¹ããã«ããŒã«ã€ããŠã¯ããã¹ããåç §ããŠãã ããã
ãã¡ã€ã«æ§æ
<bundled-plugin-root>/acme-chat/âââ package.json # openclaw.channel ã¡ã¿ããŒã¿âââ openclaw.plugin.json # èšå®ã¹ããŒããå«ããããã§ã¹ãâââ index.ts # defineChannelPluginEntryâââ setup-entry.ts # defineSetupPluginEntryâââ api.ts # å
¬éãšã¯ã¹ããŒãïŒä»»æïŒâââ runtime-api.ts # å
éšã©ã³ã¿ã€ã ãšã¯ã¹ããŒãïŒä»»æïŒâââ src/ âââ channel.ts # createChatChannelPlugin ãä»ãã ChannelPlugin âââ channel.test.ts # ãã¹ã âââ client.ts # ãã©ãããã©ãŒã API ã¯ã©ã€ã¢ã³ã âââ runtime.ts # ã©ã³ã¿ã€ã ã¹ãã¢ïŒå¿
èŠãªå ŽåïŒé«åºŠãªãããã¯
åºå®ãã¢ã«ãŠã³ãã¹ã³ãŒãããŸãã¯ã«ã¹ã¿ã ã®è¿ä¿¡ã¢ãŒã
describeMessageTool ãšã¢ã¯ã·ã§ã³æ€åº
inferTargetChatTypeãlooksLikeIdãreservedLiteralsãresolveTarget
api.runtime ãä»ãã TTSãSTTãã¡ãã£ã¢ããµããšãŒãžã§ã³ã
å ±æåä¿¡ã€ãã³ãã®ã©ã€ããµã€ã¯ã«ïŒåã蟌ã¿ã解決ãèšé²ããã£ã¹ããããå®äºåŠç
次ã®ã¹ããã
- ãããã€ã㌠Plugin - Plugin ã§ã¢ãã«ãæäŸããå Žå
- SDK ã®æŠèŠ - ãµããã¹ã€ã³ããŒãã®å®å šãªãªãã¡ã¬ã³ã¹
- SDK ã®ãã¹ã - ãã¹ããŠãŒãã£ãªãã£ãšã³ã³ãã©ã¯ããã¹ã
- Plugin ãããã§ã¹ã - å®å šãªãããã§ã¹ãã¹ããŒã