Messages and delivery
संदेश जीवनचक्र का पुनर्संरचनाकरण
यह रीफ़ैक्टर क्यों किया गया
चैनल स्टैक कई स्थानीय सुधारों से विकसित हुआ: प्रत्येक
परिपक्वता स्तर के लिए अलग इनबाउंड सहायक (सरल अडैप्टरों के लिए runtime.channel.inbound.run,
समृद्ध अडैप्टरों के लिए runtime.channel.inbound.runPreparedReply), पुराने उत्तर-डिस्पैच
सहायक (dispatchInboundReplyWithBase, recordInboundSessionAndDispatchReply),
चैनल-विशिष्ट पूर्वावलोकन स्ट्रीमिंग, और मौजूदा उत्तर-पेलोड पथों में बाद में जोड़ी गई
अंतिम-डिलीवरी स्थायित्व व्यवस्था। इस संरचना ने बहुत अधिक सार्वजनिक अवधारणाएँ और
ऐसे बहुत अधिक स्थान बना दिए जहाँ डिलीवरी के अर्थ अलग हो सकते थे।
वह विश्वसनीयता अंतराल जिसने पुनःडिज़ाइन को अनिवार्य बनाया:
Telegram पोलिंग अपडेट की अभिस्वीकृति हुई -> सहायक का अंतिम टेक्स्ट मौजूद है -> sendMessage सफल होने से पहले प्रक्रिया पुनः आरंभ होती है -> अंतिम प्रतिक्रिया खो जाती हैलक्षित अपरिवर्तनीयता: जब core यह तय कर ले कि कोई दृश्यमान आउटबाउंड संदेश मौजूद होना चाहिए, तो प्लेटफ़ॉर्म कॉल का प्रयास करने से पहले भेजने का आशय स्थायी होना आवश्यक है, और सफलता के बाद प्लेटफ़ॉर्म रसीद को कमिट करना आवश्यक है। यह डिफ़ॉल्ट रूप से कम-से-कम-एक-बार पुनर्प्राप्ति प्रदान करता है। ठीक-एक-बार व्यवहार केवल वहाँ मौजूद होता है जहाँ कोई अडैप्टर मूल आइडेम्पोटेंसी सिद्ध करता है या रीप्ले से पहले भेजने के बाद अज्ञात रहे प्रयास का प्लेटफ़ॉर्म स्थिति से मिलान करता है।
क्या जारी किया गया
आंतरिक डोमेन src/channels/message/* में स्थित है:
| फ़ाइल | उत्तरदायित्व |
|---|---|
types.ts |
अडैप्टर, प्रेषण-संदर्भ, रसीद और स्थायी-आशय प्रकार अनुबंध |
send.ts |
withDurableMessageSendContext / sendDurableMessageBatch — स्थायी प्रेषण संदर्भ |
receive.ts |
createMessageReceiveContext — इनबाउंड अभिस्वीकृति-नीति स्टेट मशीन |
live.ts |
लाइव पूर्वावलोकन स्थिति और उसी स्थान पर अंतिम रूप देने या फ़ॉलबैक करने का तर्क |
state.ts |
classifyDurableSendRecoveryState — व्यवधान के बाद पुनर्प्राप्ति वर्गीकरण |
receipt.ts |
प्लेटफ़ॉर्म प्रेषण परिणामों को MessageReceipt में सामान्यीकृत करता है |
capabilities.ts |
किसी पेलोड से आवश्यक स्थायी-अंतिम क्षमताएँ प्राप्त करता है |
contracts.ts |
घोषित अडैप्टर क्षमताओं के लिए अनुबंध-प्रमाण सत्यापन |
adapter.ts |
defineChannelMessageAdapter |
outbound-bridge.ts |
createChannelMessageAdapterFromOutbound — पुराने sendText/sendMedia/sendPayload/sendPoll फ़ंक्शनों को रैप करता है |
ingress-queue.ts |
createChannelIngressQueue — स्थायी इनबाउंड इवेंट क्यू |
durable-receive.ts |
createDurableInboundReceiveJournal — इनबाउंड डिडुप्लिकेशन के लिए स्वीकार/लंबित/पूर्ण/रिलीज़ जर्नल |
inbound-reply-dispatch.ts |
dispatchChannelInboundReply और पुराने नाम वाले रैपर |
reply-pipeline.ts |
createChannelReplyPipeline, उत्तर-उपसर्ग और टाइपिंग-कॉलबैक सहायक |
सार्वजनिक सतह: openclaw/plugin-sdk/channel-outbound (प्रेषण/रसीद/स्थायी/लाइव/उत्तर-पाइपलाइन
सहायक) और openclaw/plugin-sdk/channel-inbound (इनबाउंड संदर्भ, runChannelInboundEvent,
dispatchChannelInboundReply)। अडैप्टर उदाहरणों, वर्तमान
प्रकार नामों और माइग्रेशन टिप्पणियों के लिए वे पृष्ठ देखें — API
संरचना के लिए सत्य का स्रोत वे हैं, नीचे दी गई रूपरेखाएँ नहीं।
प्रेषण संदर्भ
withDurableMessageSendContext चैनल कोड को एक आउटबाउंड
संदेश के लिए render, previewUpdate,
send, edit, delete, commit और fail चरण देता है। sendDurableMessageBatch सामान्य स्थिति का रैपर है: रेंडर करें, भेजें,
फिर sent/suppressed पर कमिट करें या त्रुटि होने पर विफल करें।
sendDurableMessageBatch एक विभेदित परिणाम लौटाता है:
| स्थिति | अर्थ |
|---|---|
sent |
कम-से-कम एक दृश्यमान प्लेटफ़ॉर्म संदेश डिलीवर किया गया |
suppressed |
किसी भी प्लेटफ़ॉर्म संदेश को अनुपस्थित नहीं माना जाना चाहिए (हुक द्वारा रद्द, ड्राई-रन आदि) |
partial_failed |
बाद के पेलोड या साइड इफ़ेक्ट के विफल होने से पहले कम-से-कम एक संदेश डिलीवर हुआ |
failed |
कोई प्लेटफ़ॉर्म रसीद उत्पन्न नहीं हुई |
स्थायित्व required, best_effort या disabled
(src/channels/message/types.ts में MessageDurabilityPolicy) में से एक होता है। स्थायी आशय न लिखे जा सकने पर required
सुरक्षित रूप से विफल होता है; स्थायित्व अनुपलब्ध होने पर best_effort
सीधे प्रेषण पर आगे बढ़ता है; disabled रीफ़ैक्टर से पहले का
सीधा-प्रेषण व्यवहार बनाए रखता है। पुराने संगतता सहायक डिफ़ॉल्ट रूप से
disabled का उपयोग करते हैं और केवल इसलिए required का अनुमान नहीं लगाते कि किसी चैनल के पास एक सामान्य
आउटबाउंड अडैप्टर है।
वह सीमा जो जोखिमपूर्ण बनी रहती है: प्लेटफ़ॉर्म कॉल सफल होने के बाद और
रसीद कमिट होने से पहले। यदि प्रक्रिया वहाँ समाप्त हो जाती है, तो core यह नहीं जान सकता कि
प्लेटफ़ॉर्म संदेश मौजूद है या नहीं, जब तक अडैप्टर reconcileUnknownSend घोषित न करे।
यह हुक बाधित प्रेषण को sent, not_sent या
unresolved के रूप में वर्गीकृत करता है; केवल not_sent रीप्ले की अनुमति देता है। मिलान-क्षमता रहित चैनल
unknown_after_send स्थिति (src/channels/message/state.ts,
src/infra/outbound/delivery-queue-recovery.ts) पर वापस जाते हैं और कम-से-कम-एक-बार
रीप्ले केवल तभी चुन सकते हैं जब डुप्लिकेट दृश्यमान संदेश उस चैनल के लिए स्वीकार्य और दस्तावेज़ीकृत
समझौता हों।
प्राप्ति संदर्भ
createMessageReceiveContext प्रत्येक इनबाउंड इवेंट की अभिस्वीकृति/नकारात्मक-अभिस्वीकृति स्थिति को
आइडेम्पोटेंट ack() और स्पष्ट nack(error) के साथ ट्रैक करता है। अभिस्वीकृति नीति
(ChannelMessageReceiveAckPolicy) इनमें से एक है:
| नीति | अभिस्वीकृति कब होती है |
|---|---|
after_receive_record |
core ने पुनःडिलीवरी का डिडुप्लिकेशन/रूटिंग करने के लिए पर्याप्त इनबाउंड मेटाडेटा स्थायी कर लिया |
after_agent_dispatch |
एजेंट रन डिस्पैच कर दिया गया |
after_durable_send |
इस टर्न के लिए स्थायी आउटबाउंड प्रेषण कमिट हो गया |
manual |
कॉलर अभिस्वीकृति का समय स्पष्ट रूप से नियंत्रित करता है (नीति घोषित न करने वाले अडैप्टरों के लिए डिफ़ॉल्ट) |
Telegram पोलिंग इसका उपयोग सुरक्षित-पूर्ण अपडेट वॉटरमार्क
(extensions/telegram/src/bot-update-tracker.ts में safeCompletedUpdateId) को स्थायी करने के लिए करती है:
grammY अब भी प्रत्येक अपडेट को मिडलवेयर शृंखला में प्रवेश करते समय देखता है, लेकिन
OpenClaw स्थायी पुनःआरंभ वॉटरमार्क को केवल उन अपडेटों से आगे बढ़ाता है
जिनका डिस्पैच पूरा हुआ था, इसलिए विफल या अब भी लंबित अपडेट पुनःआरंभ के बाद रीप्ले होते हैं।
Telegram का अपस्ट्रीम getUpdates ऑफ़सेट अब भी grammY के स्वामित्व में है; इस
वॉटरमार्क से आगे प्लेटफ़ॉर्म-स्तरीय पुनःडिलीवरी नियंत्रित करने वाला पूरी तरह
स्थायी पोलिंग स्रोत निर्मित नहीं किया गया है (खुले प्रश्न देखें)।
लाइव पूर्वावलोकन
src/channels/message/live.ts पूर्वावलोकन/संपादन/अंतिम रूप देने को एक जीवनचक्र के रूप में मॉडल करता है:
createLiveMessageState, markLiveMessagePreviewUpdated,
markLiveMessageFinalized, markLiveMessageCancelled और
deliverFinalizableLivePreviewAdapter (ड्राफ़्ट से अंतिम संपादन बनाएँ, उसे लागू
करें, और संपादन संभव न होने या विफल होने पर सामान्य प्रेषण का उपयोग करें)।
LiveMessageState.phase, idle | previewing | finalizing | finalized | cancelled है; canFinalizeInPlace नियंत्रित करता है कि कोई पूर्वावलोकन नए प्रेषण के बजाय
संपादन के माध्यम से अंतिम संदेश बन सकता है या नहीं।
स्थायी रसीदें
MessageReceipt (src/channels/message/types.ts) एकल तार्किक प्रेषण के एक या अधिक
प्लेटफ़ॉर्म संदेश आईडी को platformMessageIds तथा
प्रत्येक भाग के parts (प्रकार, इंडेक्स, थ्रेड आईडी, उत्तर-आईडी) में सामान्यीकृत करता है। थ्रेडिंग और बाद के संपादनों
के लिए एक प्राथमिक आईडी रखी जाती है। यही बहु-भागीय डिलीवरी (टेक्स्ट
और मीडिया, खंडित टेक्स्ट, कार्ड फ़ॉलबैक) को पुनःआरंभ के बाद रीप्ले और डिडुप्लिकेट करने योग्य
बनाता है।
सार्वजनिक SDK में कमी
रीफ़ैक्टर ने इन्हें समाहित या अप्रचलित किया: reply-runtime, reply-dispatch-runtime,
reply-reference, reply-chunking, सार्वजनिक
API के रूप में प्रदर्शित reply-payload सहायक, inbound-reply-dispatch, channel-reply-pipeline, और पुराने आउटबाउंड फ़साड के अधिकांश सार्वजनिक
उपयोग। src/plugin-sdk/channel-message.ts अब channel-outbound /
channel-inbound की ओर संकेत करने वाला
@deprecated पुनःनिर्यात बैरल है; channel.turn रनटाइम उपनाम हटा दिए गए और पुराना
/plugins/sdk-channel-turn दस्तावेज़ पृष्ठ
चैनल इनबाउंड API पर रीडायरेक्ट करता है। नए Plugin कोड को
सीधे channel-outbound और channel-inbound लक्षित करना चाहिए।
कार्यान्वयन मूल डिज़ाइन से कहाँ अलग हुआ
नीचे दी गई डिज़ाइन रूपरेखा वर्णित रूप में कभी जारी नहीं हुई। अभिलेख ऐतिहासिक सटीकता के लिए रखा गया है; इन प्रकार नामों को वर्तमान API न मानें।
- कोई
MessageOrigin/shouldDropOpenClawEchoनहीं। मूल योजना में Gateway-विफलता संदेशों परsource: "openclaw"मूल टैग और एक साझा प्रेडिकेट का प्रस्ताव था, जो साझा कक्षों में टैग किए गए बॉट-लेखित प्रतिध्वनि संदेशों कोallowBotsप्राधिकरण से पहले हटा देता। वह प्रकार और प्रेडिकेट कोडबेस में मौजूद नहीं हैं।allowBotsस्वयं एक वास्तविक प्रति-चैनल कॉन्फ़िगरेशन कुंजी है (Slack, Discord, Google Chat और अन्य), लेकिन उसकी सुरक्षा के लिए बनाया जाने वाला मूल-टैगिंग तंत्र कभी निर्मित नहीं हुआ। बॉट-सक्षम कक्षों में Gateway-विफलता प्रतिध्वनि दमन अब भी एक लंबित कमी है, जारी की गई गारंटी नहीं। - कोई एकीकृत
core.messages.receive/send/live/stateनेमस्पेस नहीं। जारी किए गए फ़ंक्शनcore.messages.*फ़साड के पीछे होने के बजाय सीधेsrc/channels/message/*(withDurableMessageSendContext,createMessageReceiveContext,createLiveMessageState,classifyDurableSendRecoveryState) में स्थित हैं। - कोई सामान्य
ChannelMessage/MessageTarget/MessageRelationसामान्यीकृत संदेश प्रकार नहीं। core अब भी एक प्लेटफ़ॉर्म-निरपेक्ष संदेश संरचना औरkind: "reply" | "followup" | "broadcast" | "system"संबंध के बजाय ठोस उत्तर पेलोड (ReplyPayload) और चैनल-विशिष्ट संदर्भों को प्रेषण अडैप्टरों से गुजारता है। - अभिस्वीकृति नीति के नाम रूपरेखा से अलग हैं। जारी:
after_receive_record | after_agent_dispatch | after_durable_send | manual। मूल रूपरेखा में Webhook-टाइमआउट कारण फ़ील्ड के साथimmediate | after-record | after-durable-send | manualका उपयोग किया गया था; वह संरचना निर्मित नहीं हुई। DurableFinalDeliveryRequirementMapक्षमता कुंजियों ने रूपरेखा वालेMessageCapabilitiesऑब्जेक्ट को प्रतिस्थापित कर दिया। क्षमताएँ समतल बूलियन फ़्लैग हैं (text,media,poll,payload,silent,replyTo,thread,nativeQuote,messageSendingHooks,batch,reconcileUnknownSend,afterSendSuccess,afterCommit), जिन्हें नेस्टेडtext.chunking/attachments.voiceशैली की संरचना के बजायverifyDurableFinalCapabilityProofsके माध्यम से सत्यापित किया जाता है।
ठोस माइग्रेशन जोखिम (अब भी प्रासंगिक)
ये चैनल-विशिष्ट साइड इफ़ेक्ट रीफ़ैक्टर से पहले के हैं और नए प्रेषण पथों के माध्यम से काम करते रहने चाहिए। ये काल्पनिक नहीं हैं: प्रत्येक आज कार्यान्वित और अत्यावश्यक है।
- iMessage (
extensions/imessage/src/monitor/echo-cache.ts,persisted-echo-cache.ts): सफल प्रेषण के बाद मॉनिटर भेजे गए संदेशों को इको कैश में दर्ज करता है। टिकाऊ अंतिम प्रेषणों को फिर भी उस कैश को भरना होगा, अन्यथा OpenClaw अपने ही उत्तरों को इनबाउंड उपयोगकर्ता संदेशों के रूप में पुनः ग्रहण कर सकता है। - Tlon (
extensions/tlon/src/monitor/index.ts): एक वैकल्पिक मॉडल हस्ताक्षर जोड़ता है और समूह उत्तरों के बाद भागीदारी वाले थ्रेड दर्ज करता है। टिकाऊ डिलीवरी को इन प्रभावों को बायपास नहीं करना चाहिए। - Discord और अन्य तैयार डिस्पैचर पहले से ही प्रत्यक्ष डिलीवरी और पूर्वावलोकन व्यवहार के स्वामी हैं। कोई चैनल तब तक शुरू से अंत तक टिकाऊ नहीं होता, जब तक उसका तैयार डिस्पैचर अंतिम संदेशों को स्पष्ट रूप से प्रेषण संदर्भ के माध्यम से रूट न करे; केवल सामान्य अडैप्टर से कवरेज मानकर न चलें।
- Telegram मौन फ़ॉलबैक डिलीवरी को चंकिंग/फ़ॉलबैक प्रोजेक्शन के बाद केवल पहला पेलोड नहीं, बल्कि संपूर्ण प्रोजेक्टेड पेलोड सरणी डिलीवर करनी होगी।
- LINE, Zalo, Nostr और इसी तरह के सहायक पथों में रिप्लाई-टोकन प्रबंधन, मीडिया प्रॉक्सीकरण, भेजे गए संदेशों के कैश या केवल-कॉलबैक लक्ष्य हो सकते हैं। जब तक इन अर्थ-संबंधी व्यवहारों को प्रेषण अडैप्टर में निरूपित करके परीक्षणों द्वारा कवर नहीं किया जाता, तब तक वे चैनल-स्वामित्व वाली डिलीवरी पर बने रहते हैं।
- प्रत्यक्ष-DM सहायक में ऐसा रिप्लाई कॉलबैक हो सकता है, जो एकमात्र सही ट्रांसपोर्ट लक्ष्य हो। सामान्य आउटबाउंड को कच्चे प्लेटफ़ॉर्म फ़ील्ड से लक्ष्य का अनुमान लगाकर उस कॉलबैक को छोड़ना नहीं चाहिए।
विफलता वर्गीकरण
अडैप्टर ट्रांसपोर्ट विफलताओं को DeliveryFailureKind-शैली की बंद
श्रेणियों में वर्गीकृत करते हैं (अस्थायी, दर सीमा, प्रमाणीकरण, अनुमति, नहीं मिला, अमान्य
पेलोड, विरोध, रद्द, अज्ञात)। कोर नीति:
- अस्थायी और दर-सीमा विफलताओं पर पुनः प्रयास करें।
- अमान्य-पेलोड विफलताओं पर तब तक पुनः प्रयास न करें, जब तक कोई रेंडर फ़ॉलबैक उपलब्ध न हो।
- कॉन्फ़िगरेशन बदलने तक प्रमाणीकरण या अनुमति विफलताओं पर पुनः प्रयास न करें।
- नहीं-मिला की स्थिति में, जब चैनल इसे सुरक्षित घोषित करे, तब लाइव अंतिमकरण को संपादन से नए प्रेषण पर फ़ॉलबैक करने दें।
- विरोध की स्थिति में, यह तय करने के लिए रसीद/आइडेम्पोटेंसी स्थिति का उपयोग करें कि संदेश पहले से मौजूद है या नहीं।
- प्लेटफ़ॉर्म कॉल के सफल हो सकने के बाद, लेकिन रसीद
कमिट से पहले हुई कोई भी त्रुटि
unknown_after_sendबन जाती है, जब तक अडैप्टर यह प्रमाणित न करे कि प्लेटफ़ॉर्म ऑपरेशन हुआ ही नहीं।
खुले प्रश्न
- क्या Telegram को अंततः grammY (
1.43.0) पोलिंग रनर को ऐसे पूर्णतः टिकाऊ पोलिंग स्रोत से बदलना चाहिए, जो केवल OpenClaw के सहेजे गए पुनरारंभ वॉटरमार्क (safeCompletedUpdateId) को ही नहीं, बल्कि प्लेटफ़ॉर्म-स्तरीय पुनः डिलीवरी को भी नियंत्रित करे। - क्या लाइव पूर्वावलोकन स्थिति को अंतिम प्रेषण आशय वाले रिकॉर्ड में ही रहना चाहिए या किसी सहोदर लाइव-स्थिति स्टोर में।
- क्या साझा बॉट-सक्षम कक्षों में Gateway-विफलता इको दमन के लिए मूल रूप से नियोजित उद्गम-टैगिंग तंत्र, एक सरल प्रति-चैनल अनुबंध की आवश्यकता है, या यह दायरे से बाहर है।
- किन चैनलों में क्रॉस-बॉट इको दमन के लिए मूल उद्गम/मेटाडेटा समर्थन उपलब्ध है, और किन्हें सहेजी गई आउटबाउंड रजिस्ट्री की आवश्यकता है।