تخطَّ إلى المحتوى

جسر الدردشة

المزوّد القيمة على السلك النقل نوع المساحة
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 وحالة «جارٍ التفكير…» بينما يعمل الوكيل

يصرّح بها واصف المزوّد، وهو ما يولّد حوار الاتصال ويتحقق من الإرسال.

معرّف الحقل التسمية مطلوب البادئة أين يوجد
botToken رمز وصول البوت نعم xoxb- OAuth & Permissions
appToken رمز وصول على مستوى التطبيق نعم xapp- Basic Information → App-Level Tokens، النطاق connections:write
configRefreshToken رمز وصول تكوين التطبيق لا xoxe رموز تهيئة التطبيق على api.slack.com/apps

configRefreshToken هو بيانات اعتماد إدارة التطبيق: من دونه يعمل الاتصال، لكن لا يمكن إنشاء البوت أو تحريره من Control Center. يُدوَّر عند كل استخدام، وتُخزَّن القيمة المدوَّرة فورًا.

تُشذَّب القيم، وتُسقَط المفاتيح غير المصرَّح بها، ويُرفض اختلاف البادئة بالجملة نفسها على العميل والخادم. يجب أن تحضر الحقول المطلوبة عند كل اتصال، والحقل الاختياري المتروك فارغًا يُبقي ما كان مخزّنًا.

نطاقات رمز البوت:

app_mentions:read
assistant:write
channels:history
chat:write
commands
groups:history
im:history
users:read
users:read.email

اشتراكات أحداث البوت:

app_home_opened
app_mention
message.channels
message.groups
message.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 الأثر حين تغيب
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 بلا بطاقة مفتوحة أصلًا مساحة دافئة، فيُترك صامتًا. وتجهيز لا ينتظره أحد في الدردشة — محادثة فُتحت في التطبيق، أو محادثة عالقة تُستأنف عند الإقلاع — لا ينتج بطاقة أصلًا.

منتجات الدردشة تقبل في الرابط 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 طابق البريد الموثَّق لدى المزوّد مستخدمًا هو عضو في مساحة العمل أصلًا

كلها بنطاق مساحة العمل. الخادم بلا حالة، فكل استدعاء يحمل 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 محرفًا
عزل الفشل حدث سيئ واحد لا يُسقط الجسر؛ يبقى الاتصال يخدم كل محادثة أخرى