جسر الدردشة
المزوّدون
قسم بعنوان “المزوّدون”| المزوّد | القيمة على السلك | النقل | نوع المساحة |
|---|---|---|---|
| Slack | slack |
Socket Mode (WSS صادر) | slack |
Slack هو المزوّد الوحيد. ChatProvider تعداد بقيمة واحدة، ويسجّل cc_server مكوّنًا إضافيًا واحدًا. المخطط وكل بحث وكل فهرس فريد مفتاحها provider، فلا تعبر المعرّفات بين المزوّدين، وإضافة مزوّد تغيير على الخادم وحده.
تطبيق واحد لدى المزوّد لكل مساحة عمل × مزوّد.
أوامر الدردشة
قسم بعنوان “أوامر الدردشة”اسم الأمر خاص بكل تطبيق (cc افتراضيًا، فيكون /cc). يستطيع مسؤول إعادة تسميته، ثم يستخدم الجسر آخر اسم استُدعي به.
| الأمر | الأثر |
|---|---|
/cc help (أو /cc بلا فعل) |
يسرد ما يستطيع البوت فعله |
/cc link <code> |
يسترد رمزًا يُستخدم مرة واحدة ويربط هوية دردشة المرسِل |
/cc ticket <title> |
يفتح تذكرة محلية يبلّغ عنها العضو المربوط |
/cc ticket <title> | <description> |
الأمر نفسه، وكل ما بعد أول | هو الوصف |
| فعل مجهول | يبلّغ عن الفعل المجهول مع نص المساعدة |
طرق بلوغ وكيل:
| الوسيلة | السلوك |
|---|---|
@bot … في قناة |
يفتح خيط Slack ومساحة Control Center مطابقة؛ يرد الوكيل في خيط Slack |
| رد في خيط Slack مربوط | يتابع مساحة Control Center نفسها — بلا حاجة إلى إشارة |
| رسالة عادية في قناة | تُتجاهل (ليست موجّهة إلى البوت وليست داخل خيط Slack مربوط) |
| رسالة مباشرة إلى البوت | مساحة Control Center واحدة لكل محادثة مباشرة؛ يضبط عنوان خيط Slack وحالة «جارٍ التفكير…» بينما يعمل الوكيل |
حقول بيانات الاعتماد (Slack)
قسم بعنوان “حقول بيانات الاعتماد (Slack)”يصرّح بها واصف المزوّد، وهو ما يولّد حوار الاتصال ويتحقق من الإرسال.
| معرّف الحقل | التسمية | مطلوب | البادئة | أين يوجد |
|---|---|---|---|---|
botToken |
رمز وصول البوت | نعم | xoxb- |
OAuth & Permissions |
appToken |
رمز وصول على مستوى التطبيق | نعم | xapp- |
Basic Information → App-Level Tokens، النطاق connections:write |
configRefreshToken |
رمز وصول تكوين التطبيق | لا | xoxe |
رموز تهيئة التطبيق على api.slack.com/apps |
configRefreshToken هو بيانات اعتماد إدارة التطبيق: من دونه يعمل الاتصال، لكن لا يمكن إنشاء البوت أو تحريره من Control Center. يُدوَّر عند كل استخدام، وتُخزَّن القيمة المدوَّرة فورًا.
تُشذَّب القيم، وتُسقَط المفاتيح غير المصرَّح بها، ويُرفض اختلاف البادئة بالجملة نفسها على العميل والخادم. يجب أن تحضر الحقول المطلوبة عند كل اتصال، والحقل الاختياري المتروك فارغًا يُبقي ما كان مخزّنًا.
متطلبات تطبيق Slack
قسم بعنوان “متطلبات تطبيق Slack”نطاقات رمز البوت:
app_mentions:readassistant:writechannels:historychat:writecommandsgroups:historyim:historyusers:readusers:read.emailاشتراكات أحداث البوت:
app_home_openedapp_mentionmessage.channelsmessage.groupsmessage.imإعدادات بنيوية يفرضها Control Center حين ينشئ التطبيق أو يحرّره: تفعيل Socket Mode، ومن غير request URL، وتبويب الرسائل مفعّل وقابل للكتابة، والبوت always_online، والنطاقات والأحداث المطلوبة تُدمَج فيما يملكه التطبيق أصلًا (ولا تُطرَح أبدًا)، وأمر شرطة مائلة واحد يحمل الوصف File a ticket or link your Control Center account — العلامة التي تتعرّف أمر Control Center نفسه في بيان مُصدَّر.
حالات الاتصال
قسم بعنوان “حالات الاتصال”| الحالة | على السلك | المعنى |
|---|---|---|
| غير متصل | disconnected |
لا بيانات اعتماد مخزّنة لمساحة العمل × المزوّد هذه |
| جارٍ الاتصال… | connecting |
بيانات الاعتماد مخزّنة، والنقل يطلب الاتصال أو يعيد طلبه |
| مباشر | connected |
النقل قائم ويستقبل الأحداث |
| خطأ في الاتصال | error |
مخزّنة لكن الاتصال يفشل؛ يُعرض خطأ المزوّد نفسه إلى جانبها |
إعادة الاتصال تراجع أسي مع ارتجاف، بسقف 30 ثانية. تدوير المقبس الذي يبدأه المزوّد يعيد الاتصال فورًا من غير رفع التراجع. بيانات اعتماد مرفوضة (invalid_auth أو not_authed أو token_revoked أو account_inactive أو token_expired أو invalid_app_id) أو نطاق ناقص حالة نهائية: يتوقف الجسر عن إعادة المحاولة حتى تصل بيانات اعتماد جديدة.
قدرات المزوّد (Slack)
قسم بعنوان “قدرات المزوّد (Slack)”كُتب الجسر على أغنى سطح، ويتراجع قدرةً قدرة.
| القدرة | Slack | الأثر حين تغيب |
|---|---|---|
streaming |
نعم | يُنشر الرد كاملًا مرة واحدة، في النهاية |
streamingRequiresThread |
نعم | هدف رد بلا مرساة خيط ينشر الردود كاملة |
ephemeralMessages |
نعم | جواب أمر شرطة مائلة بلا مقبض رد صالح يُسقَط (الرفض يُنشر دائمًا داخل الخيط) |
threadStatus |
نعم | لا سطر عابر «جارٍ التفكير…» |
threadTitle |
نعم | يبقى لخيط الدردشة عنوانه الافتراضي |
slashCommands |
نعم | لا سطح /cc |
taskCards |
نعم | نص فقط — لا بطاقة فوق الرد ولا رابط «عرض في Control Center» |
maxMessageLength |
39,000 | الردود الأطول تُقطَع مع إشارة إلى Control Center |
maxStreamChunkLength |
3,800 | تُقسَم الإلحاقات عند حد فقرة أو سطر أو مسافة |
الإعلان عن البث ليس وعدًا به: بث Slack يحتاج ميزة Agents & AI Apps في خطة مدفوعة. رفض وقت الاستدعاء (streaming_not_supported أو feature_not_enabled أو paid_only أو …) يُحفظ طوال عمر الاتصال ويُعرَض على أنه streamingAvailable: false. بطاقات مهام Slack تسلك الاستدعاء نفسه، فرفض Slack لوضع عرض البطاقة يُبقي البث ويسقط البطاقة وحدها، مرة واحدة، طوال عمر الاتصال.
يجب أن يسمّي البث قارئه أيضًا. يشترط Slack مستخدمًا مستلمًا وفريقه معًا لفتح بث في قناة (الرسالة المباشرة لا تشترط ذلك)، ويرفض بـ missing_recipient_user_id / missing_recipient_team_id خلاف ذلك — رفض لكل استدعاء، فيواصل الجسر المحاولة وتصل الجولة رسالة واحدة منشورة في هذه الأثناء. المستلم هو من سأل، يُؤخذ من الرسالة الواردة (فريقه لا فريق التطبيق، فتنحل قناة Slack Connect حلًا صحيحًا). ولجولة لم يبدأها أحد من الدردشة — عمل انطلق في Control Center على محادثة مربوطة أصلًا — المستلم هو العضو الذي أنشأ ربطه الجسر؛ وإن فك ربط حساب دردشته بعد ذلك، تُنشر تلك الجولة كاملة بدل أن تُبَث.
بطاقات المهام
قسم بعنوان “بطاقات المهام”بطاقة مجمّعة واحدة لكل جولة وكيل (task_display_mode: plan)، صف لكل خطوة. كل صف task_update من id وtitle وstatus، مع details تُرسَل مرة واحدة حين تجهز الفكرة. details وoutput بالمعرّف نفسه يتسلسلان بلا فاصل، فتُرسَل details مرة واحدة ولا يُبَث output أبدًا. عنوان الخطة هو الطلب، والجواب إلحاق لاحق منفصل يحمل قطعة markdown_text.
| الجزء | المحتوى |
|---|---|
| عنوان الخطة | ما طُلب |
| صف الإعداد | Working on it… ثم Cloning … ثم Starting the agent… — صف واحد، يُستبدَل عنوانه |
| صف التفكير | Thinking…، يُضاف حين يبدأ الوكيل الاستدلال؛ الفكرة هي details هذا الصف، تُرسَل مرة واحدة حين ينتهي الاستدلال |
| صفوف الأدوات | صف لكل أداة (Bash which swift)، بمعرّف فريد حتى تقف جنبًا إلى جنب |
| الحالة | قيد التنفيذ ما دام ذلك الصف يعمل؛ مكتمل حين يبدأ الصف التالي أو تنتهي الجولة؛ خطأ عند الفشل والمقاطعة وحد الجولة |
| دعوة إلى الإجراء | View in Control Center صفًا أخيرًا حين تنتهي الجولة — غائب ما دام العمل جاريًا، وغائب حين لا عنوان عام يمكن بلوغه للخادم |
ما لا تحمله بطاقة أبدًا: مخرجات أداة، أو وسيط خارج قائمة السماح تلك، أو أي شيء يُقرأ من القاعدة. نثر الاستدلال هو تفاصيل صف Thinking… وحدها (بسقف 256 محرفًا)، لا متن الرسالة. بطاقة لا تقول جديدًا لا تُعاد. تفتح الإشارة البث فورًا حتى لا يصمت خيط Slack، والجولة التي لا تنتج نصًا أصلًا تُنهي البطاقة نفسها بملاحظة قصيرة بدل نشر رسالة ثانية. جولة بلا كلمات على مساحة لم تفتح بثًا (لا بطاقات، أو البث مرفوض أصلًا) تنشر مع ذلك رسالة واحدة تحمل البطاقة المكتملة والملاحظة.
البطاقات المبثوثة والمنشورة حمولتان مختلفتان:
| الشكل | الهيئة | النثر |
|---|---|---|
| مبثوث | قطع plan_update مع task_update مفتاحها id |
سلسلة عادية، بسقف 256 محرفًا للقطعة |
| منشور | كتلة plan تلف بطاقات task_card مفتاحها task_id |
نص غني |
معالجة الفشل:
| الموقف | السلوك |
|---|---|
| يرفض المزوّد البطاقة عند إلحاق | يُعاد الإلحاق فورًا من دونها؛ يصل النثر مباشرًا، وتُسقَط البطاقات طوال عمر الاتصال |
| يرفض المزوّد النثر أيضًا | يُغلق البث ويُنشر الرد كاملًا |
| ينقطع البث بعد أن وصل جزء من الرد | يُغلق البث ولا يُعاد نشر شيء |
انظر جسور الدردشة لسبب تقسيم الحمولة على هذا النحو.
الإعداد، قبل أن توجد جولة
قسم بعنوان “الإعداد، قبل أن توجد جولة”تُفتح البطاقة حين تُقبل الإشارة، قبل الاستنساخ وقبل أول قطعة من الجواب؛ والجولة التي تلي تتبنّى البطاقة والبث نفسيهما. السرد بعد ذلك يأتي من SpaceProvisioningChanged على ناقل أحداث النطاق — التقدم نفسه الذي تقرأه لافتة التجهيز في تطبيق الحاسوب من صف المساحة.
| حالة التجهيز | السرد |
|---|---|
| قُبلت الإشارة | Working on it… |
الخطوة repo |
Cloning <repo>… |
الخطوة prCheckout |
Checking out the pull request in <repo>… |
الخطوة script |
Running the setup script for <repo>… |
الخطوة agent |
Setting up <agent>… |
provisioning، بلا خطوة بعد |
Preparing the workspace… |
ready |
Starting the agent… |
| استدلال الوكيل | Thinking… |
failed |
Workspace setup failed — details are in Control Center. |
cancelled |
Workspace setup was stopped — details are in Control Center. |
سرد الإعداد يشترط الشروط الأربعة كلها:
| الشرط |
|---|
| المزوّد يعرض البطاقات |
| البث متاح على هذا الاتصال |
| للمساحة طلب دردشة قيد التنفيذ |
| لا بطاقة تبلّغ عن جولة هناك أصلًا |
يبقى الطلب قيد التنفيذ حتى تبدأ جولة أو يفشل الإعداد. حدث ready بلا بطاقة مفتوحة أصلًا مساحة دافئة، فيُترك صامتًا. وتجهيز لا ينتظره أحد في الدردشة — محادثة فُتحت في التطبيق، أو محادثة عالقة تُستأنف عند الإقلاع — لا ينتج بطاقة أصلًا.
صفحة الارتداد /open
قسم بعنوان “صفحة الارتداد /open”منتجات الدردشة تقبل في الرابط http(s) ولا تقبل غيره، فلا تستطيع دعوة إلى إجراء أن تسمّي مخطط تطبيق الحاسوب مباشرة.
| الخاصية | القيمة |
|---|---|
| المسار | GET /open/workspaces/<workspaceId>/(spaces|tickets)/<id> |
| الأصل | يُشتق من العنوان العام للخادم (ws → http، wss → https)؛ يُبقى عنوان loopback |
| الاستجابة | صفحة HTML صغيرة بلا تخزين مؤقت تنتقل إلى control-center://workspaces/<workspaceId>/(spaces|tickets)/<id> وتعرض زرًا يدويًا |
| التحقق | يجب أن تطابق المعرّفات [A-Za-z0-9_.-]{1,128}؛ وأي شيء آخر 404 |
| الوصول | عام وخامل — لا يقرأ قاعدة، فليس كاشف وجود؛ والتفويض يقوم به التطبيق الذي يفتحه |
حقول ملف البوت
قسم بعنوان “حقول ملف البوت”| الحقل | الحد | ملاحظات |
|---|---|---|
appName |
1–35 محرفًا | الافتراضي Control Center · <workspace name> |
botDisplayName |
1–80 محرفًا | الافتراضي control-center |
description |
≤ 140 محرفًا | |
agentDescription |
≤ 300 محرف | يظهر فوق رسالة مباشرة مع البوت |
command |
≤ 32 محرفًا | يُطبَّع: تُنزَع الشرطات المائلة البادئة، ويُحوَّل إلى أحرف صغيرة، ويُحذف [^a-z0-9_-]، والفارغ يرجع إلى cc |
agentEnabled |
— | هل تُعرَض تجربة الرسالة المباشرة والوكيل |
صندوق فارغ يعني «دعه كما هو»، لا «امحه». الحدود حدود Slack، وهي الأضيق بين المنتجات المنمذَجة.
رموز الربط
قسم بعنوان “رموز الربط”| الخاصية | القيمة |
|---|---|
| الطول | 6 محارف |
| الأبجدية | 23456789ABCDEFGHJKLMNPQRSTVWXYZ (بلا 0/O، و1/I، وU) |
| مدة الصلاحية | 15 دقيقة |
| الاستخدامات | واحد |
| التخزين | في الذاكرة — إعادة تشغيل الخادم تُبطل الرموز القائمة |
| النطاق | مساحة عمل واحدة ومزوّد واحد؛ إصدار رمز جديد يُبطل رمز العضو السابق |
تُطابَق الاستردادات من غير حساسية لحالة الأحرف. الرموز المجهولة والمنتهية والخاصة بمساحة عمل أخرى وذات المزوّد الخطأ تفشل كلها بالطريقة نفسها، عمدًا.
طرائق الربط
قسم بعنوان “طرائق الربط”| الطريقة | على السلك | الكيفية |
|---|---|---|
| الرمز | code |
استرد العضو /cc link <code> |
| البريد | email |
طابق البريد الموثَّق لدى المزوّد مستخدمًا هو عضو في مساحة العمل أصلًا |
عمليات RPC
قسم بعنوان “عمليات RPC”كلها بنطاق مساحة العمل. الخادم بلا حالة، فكل استدعاء يحمل workspace_id الخاص به في الوسائط؛ يرفض الموزّع عملية بنطاق مساحة عمل تغفله، قبل أن تُفتح أي قاعدة. بيانات الاعتماد تسافر إلى الداخل فقط — قراءات الحالة لا تُرجعها أبدًا.
| العملية | النوع | الحد الأدنى للدور | فئات الإجراء |
|---|---|---|---|
chat.providers |
قراءة | guest |
— |
chat.status |
قراءة | guest |
— |
chat.connect |
تعديل | admin |
secretAccess، networkEgress |
chat.disconnect |
تدميري | admin |
secretAccess |
chat.listUserLinks |
قراءة | guest |
— |
chat.beginUserLink |
تعديل | member |
— |
chat.unlinkUser |
تعديل | member لنفسك، وadmin لأي شخص آخر |
— |
chat.createApp |
تعديل | admin |
secretAccess، networkEgress |
chat.setupLink |
قراءة | admin |
— |
chat.botProfile |
قراءة | admin |
networkEgress |
chat.updateBotProfile |
تعديل | admin |
networkEgress |
ترث عمليات القراءة حد guest من نوعها، والتعديلات التي بلا حد صريح تحتاج member.
يؤلّف chat.setupLink عنوان إنشاء تطبيق لدى المزوّد من ملف البوت. لا يصرّح بفئة إجراء لأنه لا يقرأ سرًا ولا يطلب اتصالًا — متصفح المستخدم هو من يجري الاستدعاء الوحيد.
استعلام تفاعلي واحد يتمّم السطح:
| الاستعلام | النوع | ما يبثّه |
|---|---|---|
chat.watchUserLinks |
مراقبة | القائمة، تُدفَع من جديد كلما أُنشئ ربط أو أُزيل |
يوجد لأن الربط يحدث في الجانب الآخر: يكتب العضو الرمز الذي يُستخدم مرة واحدة في البوت، فلا يستطيع رد عميل أن يحمل الجواب عائدًا. قائمة الإعدادات وحوار «ربط حسابي» المفتوح يتبعانه معًا، وينتقل الحوار إلى تأكيد لحظة وصول ربط العضو نفسه.
يُصدر chat.beginUserLink دائمًا رمزًا لهوية المستدعي نفسه. لا يُرجع chat.updateBotProfile قيمة remaining_step إلا حين تبقى خطوة (إعادة تثبيت في العادة)، وغيابها يعني أن التحرير ساري.
يُرجع chat.providers إدخالًا لكل مزوّد معروض — الواصف، وحالة الاتصال حين توجد. كل ما تعرضه واجهة الإعدادات يأتي من تلك الحمولة، ولهذا تكون إضافة مزوّد تغييرًا على الخادم وحده.
التخزين
قسم بعنوان “التخزين”| ماذا | أين |
|---|---|
| بيانات اعتماد الاتصال | <dataDir>/<workspaceId>/chat_credentials/<provider>.json |
| إعداد موجّه لم يكتمل | <dataDir>/<workspaceId>/chat_credentials/<provider>_setup.json |
| ملفات Slack القديمة | <dataDir>/<workspaceId>/slack_credentials.json وslack_app_setup.json، تُقرأ مرة واحدة وتُرحَّل في موضعها |
تعيش بيانات الاعتماد إلى جانب قاعدة مساحة العمل لا داخلها: حذف مساحة العمل يأخذ رموز الدردشة معها، وworkspace.export أو النسخ الاحتياطي ينسخ workspace.db — فمساحة عمل تُسلَّم إلى شخص آخر لا تحمل رموز بوت حيّة. الكتابة ذرية (ملف مؤقت ثم إعادة تسمية) ومقيّدة بالمالك وحده.
جداول القاعدة
قسم بعنوان “جداول القاعدة”كلاهما في قاعدة مساحة العمل، وهي تحمل خط أساس v1 مدموجًا — فقراءة تعبر مساحات العمل لا تُصرَّف.
chat_space_links — محادثة خارجية واحدة ↔ مساحة Control Center واحدة:
| العمود | ملاحظات |
|---|---|
id |
UUID v4 |
workspace_id |
نطاق مساحة العمل |
provider |
ChatProvider.wire |
external_team_id |
فريق Slack T…، وفارغ للمزوّدين بلا هذا المفهوم |
external_channel_id |
قناة Slack C…، ومجموعة G…، ورسالة مباشرة D… |
external_thread_id |
مرساة خيط Slack، أو null حين تكون المحادثة كلها هي المرساة (رسائل البوت المباشرة) |
cc_space_id |
REFERENCES spaces(id) ON DELETE CASCADE |
created_by_user_id، created_at، last_activity_at |
فريد على (workspace_id, provider, external_channel_id, external_thread_id)، مع فهرس جزئي يفرض الأمر نفسه لمعرّفات الخيط ذات القيمة null (يعامل SQLite القيم NULL على أنها متمايزة).
chat_user_links — عضو خارجي واحد ↔ مستخدم Control Center واحد:
| العمود | ملاحظات |
|---|---|
id |
UUID v4 |
workspace_id |
نطاق مساحة العمل |
provider |
ChatProvider.wire |
external_team_id، external_user_id |
عضو Slack U… |
user_id |
مستخدم Control Center (عام، فلا مفتاح خارجي) |
method |
code أو email |
linked_at |
فريد على (workspace_id, provider, external_team_id, external_user_id) وعلى (workspace_id, provider, user_id) — إسناد بقيمة واحدة في الاتجاهين، ويستطيع الشخص الواحد أن يُربَط لدى عدة مزوّدين.
الربط حل للهوية، لا منحة وصول: تُفحص العضوية من جديد عند كل حدث وارد، فربط بعضو أُزيل بعد ذلك يُحَل إلى رفض.
نوع المساحة وبيانات وصف الرسالة
قسم بعنوان “نوع المساحة وبيانات وصف الرسالة”تُنشأ المساحة المربوطة بقيمة SpaceKind لدى المزوّد (slack). تتصرف كمساحة عادية تواجه الإنسان — تُنبَّه وتُقسَّم كـ topic — وisExternalChat هو الفحص الذي يمر به كل سلوك (العكس، والشارات، والاحتفاظ).
كل رسالة يجلبها الجسر تحمل ختم metadata['chat']:
{ "chat": { "provider": "slack", "teamId": "T024BE7LD", "channelId": "C0G9QF9GW", "threadId": "1700000000.000100", "messageId": "1700000000.000200", "userId": "U024BE7LH", "userLabel": "sam" }}الختم حارس الصدى أيضًا: تتجاوز المرآة الصادرة أي رسالة تحمله، وهذا ما يمنع رسالة دردشة واردة من أن تُنشر عائدة كما هي.
سلوك الترحيل
قسم بعنوان “سلوك الترحيل”| السلوك | التفصيل |
|---|---|
| ما يُرحَّل إلى الخارج | نص الوكيل فقط، مع سطر حالة بطاقة المهمة. يبقى نثر الاستدلال ومخرجات الأداة في Control Center |
| إيقاع البث | مخنوق إلى تفريغ واحد في الثانية ما دامت الجولة جارية؛ تسافر البطاقة والنص الذي تفسّره في الاستدعاء نفسه |
| رسائل الإنسان المكتوبة في Control Center | تُعكَس إلى الخيط المربوط، مسبوقة باسم عرض المؤلف |
| الجولات الفارغة | تُبلَّغ كملاحظة قصيرة (فشل التشغيل، أو قُوطع، أو توقف عند حد جولته) بدل الصمت |
| إزالة التكرار | مفتاحها معرّف الحدث الخاص بالمزوّد؛ يُتذكَّر آخر 512 |
| وكيل الاستقبال | محادثة مربوطة جديدة توقظ وكيل CEO في مساحة العمل، وإلا وكيلها الأول، وإلا لا أحد |
| تسمية المساحة | #channel · <request> لخيط قناة Slack، وSlack DM · <request> لرسالة مباشرة، والعناوين بسقف 48 محرفًا |
| عزل الفشل | حدث سيئ واحد لا يُسقط الجسر؛ يبقى الاتصال يخدم كل محادثة أخرى |
انظر أيضًا
قسم بعنوان “انظر أيضًا”- إعداد تكامل Slack
- ربط حساب Slack
- تخصيص بوت الدردشة
- جسور الدردشة
- الضوابط لفئات الإجراء التي تصرّح بها العمليات