مواد پر جائیں

چیٹ برجز

چیٹ برج کسی بیرونی چیٹ پروڈکٹ — آج کل Slack — کو Control Center کے ورک اسپیس میں داخل ہونے کا پہلی درجے کا ذریعہ بناتا ہے۔ بوٹ کا ذکر کریں تو ایجنٹ جاگ اُٹھتا ہے؛ Slack تھریڈ میں جواب دیں تو گفتگو جاری رہتی ہے؛ سلیش کمانڈ لکھیں تو ٹکٹ درج ہو جاتا ہے۔ اِس کے پیچھے وہی اسپیسز، ٹرانسکرپٹس، ٹکٹس اور انتساب ہوتے ہیں جو ڈیسک ٹاپ ایپ استعمال کرتی ہے۔ چیٹ ورک اسپیس پر ایک سطح ہے، اُس کی نقل نہیں۔

صرف Slack ہی واحد فراہم کنندہ ہے جو دستیاب ہے۔ پروڈکٹ میں کہیں بھی Discord، Telegram، Teams یا Matrix کا کوئی نفاذ نہیں ہے۔ یہ فیچر جان بوجھ کر فراہم کنندہ سے بے نیاز لکھا گیا ہے — دوسرا پروڈکٹ شامل کرنا ایک enum ویلیو، ایک اڈاپٹر اور ایک پلگ اِن کا کام ہے — مگر آج ”چیٹ“ سے مراد Slack ہے۔

اسے دو سطحیں محیط ہیں اور وہ مختلف لوگوں کی ہیں۔ ورک اسپیس کو Slack سے جوڑنا اور بوٹ کو حسب ضرورت بنانا ترتیبات → ورک اسپیس → عام کے تحت ایڈمن کارروائیاں ہیں۔ اپنے چیٹ اکاؤنٹ کو اپنی Control Center شناخت سے لنک کرنا ترتیبات → ورک اسپیس → پروفائل اور شناخت کے تحت رکن کی کارروائی ہے۔

تین فیصلے پورے فیچر کی تشکیل کرتے ہیں: کنکشن کس سمت چلتا ہے، باہر سے آنے والے پیغام کو کیا اجازت دیتا ہے اور پروڈکٹ سے مخصوص علم کہاں رہنے کی اجازت رکھتا ہے۔

برج کبھی فراہم کنندہ کے لیے سنتا نہیں۔ Slack تک رسائی Socket Mode کے ذریعے ہوتی ہے: سرور Slack سے ایک ساکٹ URL مانگتا ہے اور باہر کال کرتا ہے، پھر اُسی کنکشن پر ذکر، DMs اور سلیش کمانڈز وصول کرتا ہے۔

نتیجہ ہی اصل مقصد ہے۔ NAT کے پیچھے ایک لیپ ٹاپ، بغیر ڈومین کا گھریلو سرور، نیٹ ورکس کے درمیان منتقل ہونے والی مشین — یہ سب برج بغیر کسی اِن باؤنڈ پورٹ، بغیر ٹنل، بغیر TLS سرٹیفکیٹ اور بغیر کسی ایسے عوامی URL کے چلا سکتی ہیں جسے تبدیلی پر بدلنا پڑے۔ محفوظ کرنے کے لیے کوئی ویب ہک اینڈ پوائنٹ نہیں ہے کیونکہ کوئی ویب ہک اینڈ پوائنٹ ہے ہی نہیں۔ جب Control Center ایپ ڈیفینیشن ترتیب دیتا ہے تو وہ Socket Mode لازم ٹھہراتا ہے اور کسی بھی درخواست URL کو ہٹا دیتا ہے، کیونکہ دونوں کے لیے کنفیگر کی گئی ایپ ایسی ایپ ہوگی جو آدھی چلتی ہو۔

قیمت ایک کنکشن کو زندہ رکھنا ہے، اور یہ سرور کا کام ہے: دوبارہ کنیکٹ ہونا jitter کے ساتھ exponential backoff ہے جس کی حد 30 سیکنڈ ہے، فراہم کنندہ کی شروع کردہ ساکٹ روٹیشن اُس backoff کو بڑھائے بغیر دوبارہ کنیکٹ ہو جاتی ہے، اور مسترد شدہ اسناد حتمی ہوتی ہیں — منسوخ شدہ ٹوکن کو ہمیشہ دوبارہ آزمانا صرف ایک rate limit ہی کمائے گا، چنانچہ برج رک جاتا ہے اور نئے ٹوکنز کا انتظار کرتا ہے جبکہ ترتیبات میں فراہم کنندہ کی اپنی غلطی لفظ بلفظ دکھاتا ہے۔

بغیر چیٹ ایپ والا ورک اسپیس بالکل مفت پڑتا ہے: نہ ساکٹ، نہ ٹائمر، نہ ٹریفک۔ اور یہ سارا عمل سرور کے ready banner کے بعد شروع ہوتا ہے، کیونکہ جواب دینے میں سست چیٹ ایپ کو کبھی بوٹ میں تاخیر نہیں کرنی چاہیے۔

رکنیت اجازت دیتی ہے، ٹوکن کی ملکیت نہیں

سیکشن کا عنوان “رکنیت اجازت دیتی ہے، ٹوکن کی ملکیت نہیں”

جس شخص نے ایپ انسٹال کی وہ پیغام بھیجنے والا شخص نہیں ہے۔ چنانچہ کچھ بھی ہونے سے پہلے ہر اِن باؤنڈ ایونٹ ایک پرنسپل میں حل ہوتا ہے اور ناکامی کی صورت میں وضاحت کے ساتھ انکار کر دیتا ہے:

  • غیر لنک شدہ چیٹ اکاؤنٹ کو بتایا جاتا ہے کہ کیسے لنک کرنا ہے، اُسی Slack تھریڈ میں جس میں اُس نے لکھا۔
  • ایسا لنک شدہ اکاؤنٹ جس کا صارف اِس ورک اسپیس کا رکن نہیں، اُسے کہا جاتا ہے کہ ایڈمن سے دعوت مانگے۔
  • ایک رکن جس کا کردار صرف پڑھنے کا ہو (viewer, guest) کو بتایا جاتا ہے کہ اُس کا کردار کام شروع نہیں کر سکتا۔

اِن میں سے کوئی بھی آگے نہیں بڑھتا۔ چیٹ پیغام کبھی ایسے شخص سے منسوب نہیں ہوتا جس نے اُسے بھیجا نہ ہو — اور یہی چیز آڈٹ ٹریل، فی پرنسپل rate limits اور ٹکٹ رپورٹرز کو سچا رکھتی ہے۔ اِس رکنیت ماڈل کے لیے دیکھیں ملٹی پلیئر — شناخت، رکنیت اور موجودگی۔

لنک کرنا شناخت کا تعین ہے، رسائی کا عطیہ نہیں۔ رکنیت ہر ایونٹ پر دوبارہ جانچی جاتی ہے، اس لیے کسی ایسے شخص سے تعلق رکھنے والا لنک جو بعد میں ہٹا دیا گیا ہو، اُس کے پرانے مراعات کے بجائے انکار میں حل ہوتا ہے۔ لنک قائم کرنے کے دو راستے ہیں: ایک مختصر مدت کا واحد استعمال کا کوڈ جو رکن چیٹ میں لکھتا ہے، یا فراہم کنندہ کے تصدیق شدہ ای میل پر خودکار مماثلت — اور ای میل کا راستہ پھر بھی لازم ٹھہراتا ہے کہ مماثل صارف پہلے سے رکن ہو، چنانچہ ایسا اجنبی جس کا پتہ سرور پر محض موجود ہو، کسی چیز سے لنک نہیں ہوتا۔

کوڈز دانستہ طور پر میموری میں رکھے جاتے ہیں، 15 منٹ کے ”time to live“ کے ساتھ۔ کسی کو محفوظ کرنا ایسی چیز کے لیے ایک پائیدار، اسناد نما قطار بنا دیتا جو بھاپ بن جانے کے لیے ہے، اور سرور کا دوبارہ آغاز ہر بقایا کوڈ کو باطل کر دینا درست رویہ ہے، کوئی رجعت نہیں — رکن دوبارہ ”لنک“ دباتا ہے۔

انکار کا جواب اُسی Slack تھریڈ میں دیا جاتا ہے جسے پیغام نے کھولا، بطور حقیقی جواب۔ تھریڈ میں دیا گیا جواب اُس پیغام پر بیج لگاتا ہے جس کا وہ جواب ہے، اُس ایک شخص تک پہنچتا ہے جسے اُس کی ضرورت ہے، اور پھر بھی ”آپ لنک شدہ نہیں“ کو خود چینل سے باہر رکھتا ہے۔ پیغام بھیجنا بھی وہ واحد صلاحیت ہے جو ہر فراہم کنندہ کے پاس ہوتی ہے، اس لیے وضاحت کبھی کسی ایسے فیچر پر منحصر نہیں ہوتی جو ایپ کے پاس نہ ہو۔

جو برج دونوں طرف آئینہ بناتا ہے وہ لامحدود لوپ میں پڑ جائے گا جب تک کوئی چیز دونوں سمتوں میں فرق نہ کرے۔ برج جو بھی پیغام اندر لاتا ہے اُس پر metadata['chat'] کی مہر لگتی ہے — فراہم کنندہ، ٹیم، گفتگو، تھریڈ، پیغام اور بھیجنے والا۔ باہر جانے والا آئینہ ایسا کوئی بھی پیغام چھوڑ دیتا ہے جس پر یہ مہر ہو۔

یہی ایک قاعدہ دو طرفہ آئینہ بندی کو محفوظ بناتا ہے: Control Center میں ٹائپ کیا گیا انسانی پیغام لنک شدہ تھریڈ کی طرف منتقل کر دیا جاتا ہے (مصنف کے نام کے ساتھ سابقے کے طور پر)، جبکہ چیٹ سے آنے والا پیغام وہیں رک جاتا ہے جہاں وہ داخل ہوا۔

باہر جانے والی چیز جان بوجھ کر تنگ ہے۔ ایجنٹ کا جواب عام چیٹ متن کے طور پر منتقل ہوتا ہے۔ ایک مختصر سوچ کارڈ کی Thinking… قطار پر ظاہر ہو سکتی ہے۔ ٹول کا آؤٹ پٹ ڈفز کی دیوار ہوتا ہے اور Control Center میں ہی رہتا ہے، اور کٹے ہوئے جواب کا اشارہ بھی وہیں جاتا ہے۔

کارڈ کام کی رپورٹ دیتا ہے؛ متن پھر بھی پیغام ہے

سیکشن کا عنوان “کارڈ کام کی رپورٹ دیتا ہے؛ متن پھر بھی پیغام ہے”

جہاں فراہم کنندہ اُنہیں رینڈر کرتا ہے، ایک باری میں ایک ٹاسک کارڈ ہوتا ہے، جو جب تک باری چلتی ہے اپنی جگہ ترمیم ہوتا رہتا ہے اور اُس جواب کے اوپر بیٹھتا ہے جس کی وہ وضاحت کرتا ہے۔

کارڈ ایک گروہ بند پلان ہے، کوئی روندھا ہوا پیراگراف نہیں۔ Slack ایک ہی ٹاسک پر details اور output جوڑ دیتا ہے، اس لیے قطار کی تفصیلات ایک بار بھیجی جاتی ہیں (Thinking… پر سوچ) اور output کبھی اسٹریم نہیں ہوتا۔ سیٹ اپ ایک قطار ہے جس کا عنوان بدلا جاتا ہے (Working on it…, Cloning …)。 استدلال ایک Thinking… قطار شامل کرتا ہے۔ ہر ٹول اپنی الگ قطار ہے (Read lib/main.dart)。 جواب کارڈ کے نیچے عام متن ہے — Slack کا plan view اُسے بعد میں شامل ہونے والے مارک ڈاؤن سے پڑھتا ہے، نہ کہ کاموں میں ملی ہوئی کسی چنک سے۔ اِس کے علاوہ کچھ نہیں: نہ ٹول کا آؤٹ پٹ اور صرف ایک مختصر allow-list سے ایک ٹول آرگومنٹ، کیونکہ کسی نامعلوم ٹول کے آرگومنٹس پوری فائل کا باڈی ہو سکتے ہیں اور قطار ایک سطر ہوتی ہے۔

کارڈ اُس انتظار کو بھی ڈھانپتا ہے جو باری سے پہلے ہوتا ہے۔ پہلا ذکر گفتگو بناتا ہے اور اُسے بنانا ہر اُس ریپو کو کلون کرتا ہے جس میں ایجنٹ کام کرے گا — جو ایک حقیقی ریپوزٹری پر کئی منٹ ہوتے ہیں جہاں ایجنٹ سے ابھی کچھ پوچھا بھی نہیں گیا ہوتا اور تھریڈ ایسا لگتا ہے جیسے بوٹ نے درخواست نظر انداز کر دی۔ چنانچہ کارڈ اُسی لمحے کھل جاتا ہے جب ذکر قبول ہوتا ہے (Working on it…) اور پھر وہ سیٹ اپ قطار اُنہی الفاظ میں اپ ڈیٹ ہوتی ہے جو ڈیسک ٹاپ دکھاتا ہے — Cloning acme/widgets…, Setting up Reviewer…, Starting the agent… — پھر جیسے ہی ایجنٹ استدلال شروع کرتا ہے ایک Thinking… قطار، پھر ہر ٹول کے لیے ایک قطار۔ ایک کارڈ، ایک پیغام، ”میں نے سن لیا“ سے لے کر نیچے عام متن کے طور پر جواب تک۔ سیٹ اپ کی رپورٹ صرف وہاں دی جاتی ہے جہاں چیٹ میں کوئی اُس کا منتظر ہو: ایپ میں تیار کی جا رہی گفتگو، یا سرور بوٹ پر دوبارہ شروع کی گئی بے سہارا گفتگو، بغیر کسی کارڈ کے تیار ہوتی ہے۔

وہ واحد چیز اُٹھائے ہوئے ہے جو چیٹ قاری کو نہیں دے سکتی: جواب کے پیچھے والی گفتگو کا View in Control Center لنک۔ چیٹ پروڈکٹس لنک میں http(s) قبول کرتے ہیں اور کچھ نہیں، اس لیے بٹن ایک چھوٹے صفحے کی طرف اشارہ کرتا ہے جو یہ سرور پیش کرتا ہے، اور وہ صفحہ براؤزر کو ڈیسک ٹاپ ایپ کے control-center:// لنک پر بھیج دیتا ہے۔ وہ صفحہ نہ کچھ پڑھتا ہے نہ کچھ حل کرتا ہے — اُس کے URL میں موجود ids صرف ایک ڈیپ لنک میں منتقل ہوتے ہیں اور کہیں نہیں — اور بغیر قابلِ رسائی عوامی URL والا سرور ٹوٹے بٹن کے بجائے محض بغیر بٹن کا کارڈ پیدا کرتا ہے۔

کارڈز بھی ہر اُس دوسری چیز کی طرح ایک صلاحیت ہیں۔ اُن کے بغیر فراہم کنندہ بالکل پہلے کی طرح برتاؤ کرتا ہے، صرف متن، اور کال کے وقت کوئی کارڈ مسترد کرنے والا فراہم کنندہ اپنی اسٹریم برقرار رکھتا ہے: جواب ہمیشہ اُس کارڈ سے اوپر رہتا ہے جو اُس کی وضاحت کرتا ہے۔ جب اسٹریمنگ مکمل طور پر بند ہو، تب مکمل کارڈ بھیجے گئے واحد پیغام کے ساتھ سوار ہو جاتا ہے۔ درج شدہ ٹکٹ کے ساتھ بھی ایسا ہی سلوک ہوتا ہے — /cc ticket کی تصدیق ایک مکمل کارڈ ہوتی ہے جس کے وجود کی وجہ ابھی بنائے گئے ٹکٹ کا لنک ہے۔

صلاحیتیں پہلے طے ہوتی ہیں، پھر یاد رکھی جاتی ہیں

سیکشن کا عنوان “صلاحیتیں پہلے طے ہوتی ہیں، پھر یاد رکھی جاتی ہیں”

چیٹ پروڈکٹس اِس میں مختلف ہیں کہ وہ کیا رینڈر کر سکتے ہیں، اس لیے برج سب سے بھرپور سطح کے مطابق لکھا گیا ہے — ایک جواب جو تھریڈ کے اندر زندہ بڑھتا ہے، ایک عارضی ”سوچ رہا ہے…“ اسٹیٹس، ایک عنوان والا تھریڈ، سلیش کمانڈ کا فوری جواب — اور ایک وقت میں ایک صلاحیت گھٹاتا جاتا ہے۔ جس فراہم کنندہ کے پاس اِن میں سے کچھ بھی نہ ہو وہ پھر بھی چلتا ہے؛ جوابات بس ایک پیغام کے طور پر آ جاتے ہیں۔

صلاحیت کا اعلان کرنا اُس کا وعدہ کرنا نہیں ہے۔ Slack کے اسٹریمنگ جوابات کو پیڈ پلان کا فیچر درکار ہوتا ہے اور Slack صرف کال کے وقت انکار کرتا ہے۔ مستقل قرار دیا گیا انکار کنکشن کی پوری عمر کے لیے یاد رکھا جاتا ہے: برج پوچھنا چھوڑ دیتا ہے اور اُس کے بعد مکمل جوابات بھیجتا ہے اور ترتیبات یہ صاف بتا دیتی ہیں، کیونکہ ”میرے جوابات اسٹریم ہونا بند ہو گئے“ کا جواب لاگ پڑھے بغیر ملنا چاہیے۔ اگر Slack کوئی ٹاسک کارڈ مسترد کرے تو اُنہیں بھی اِسی طرح گرا دیا جاتا ہے۔

ہر حد ریلے کے بجائے فراہم کنندہ کی ہوتی ہے: پیغام کی بالائی حد، اسٹریم چنک کی بالائی حد اور یہ کہ کسی اسٹریم کو تھریڈ اینکر درکار ہے یا نہیں — یہ سب اڈاپٹر بیان کرتا ہے، اور طویل جواب پیراگراف، سطر یا لفظ کی حد پر تقسیم ہوتا ہے، لفظ کے بیچ سے نہیں۔

ہر پروڈکٹ نما چیز ایک پورٹ کے پیچھے رہتی ہے۔ عمومی کور markdown، چیٹ گفتگوؤں، چیٹ تھریڈز اور ارکان کو جانتا ہے؛ وہ Slack envelopes، mrkdwn یا gateway intents کے بارے میں کچھ نہیں جانتا۔ تین ٹکڑے یہ کام کرتے ہیں:

  • اڈاپٹر ایک پروڈکٹ کو معمول پر لاتا ہے: اُس کی ٹرانسپورٹ، اُس کی API کالیں، اُس کا متن کا کوڈیک اور اُس کے envelope کی نرالیاں۔ Slack کے قابلِ ذکر ہیں — چینل کا ذکر دو بار آتا ہے (app_mention کے طور پر اور message کے طور پر) اور بیشتر میسج ذیلی اقسام نئی درخواستیں ہونے کے بجائے آرائش ہوتی ہیں۔ دونوں اڈاپٹر میں حل ہو جاتے ہیں، اس لیے کور کبھی نہیں جان پاتا کہ Slack دو بار ڈیلیور کرتا ہے۔
  • ڈسکرپٹر فراہم کنندہ کی عوامی شکل ہے: اُسے کون سی اسناد درکار ہیں، اُن کے فارمیٹ کیا ہیں، کون سے URLs صارف کو اُنہیں ڈھونڈنے میں مدد دیتے ہیں، وہ کیا کر سکتا ہے اور کیا اُس کی ایپ یہاں سے بنائی یا ترمیم کی جا سکتی ہے۔ ترتیبات کا کارڈ، کنیکٹ ڈائیلاگ اور آرگومنٹ کی توثیق سب اُسی سے تولید ہوتے ہیں — کلائنٹ واپس ایک فراہم کنندہ کے ساتھ فیلڈ id سے منسلک ایک نقشہ بھیجتا ہے۔
  • پلگ اِن سرور کی طرف کی ترکیب ہے: اسناد کس طرح ایک تصدیق شدہ کنکشن بنتی ہیں، اڈاپٹر کیسے بنتا ہے اور فراہم کنندہ کی طرف کی ایپ کیسے منظم ہوتی ہے۔

چنانچہ پروڈکٹ شامل کرنا ایک enum ویلیو، ایک اڈاپٹر اور ایک پلگ اِن ہے۔ نہ کوئی نیا RPC op، نہ اسکیما میں تبدیلی، نہ کلائنٹ میں تبدیلی — اور کلائنٹ سچا رہتا ہے، کیونکہ جس فیلڈ کا اُس نے کبھی نام نہ سنا ہو وہ پھر بھی اُس لیبل کے ساتھ رینڈر ہوتا ہے جو سرور نے دیا۔

دو تفصیلیں اِس تجرید کو اوپر کی طرف رسنے سے روکتی ہیں۔ وہ ایپ مینجمنٹ کے قدم جن کے لیے فراہم کنندہ کے پاس کوئی API نہیں (ٹوکن بنائیں، ایپ انسٹال کریں، اجازت کی تبدیلی کے بعد دوبارہ انسٹال کریں) اپنے لنکس والے نامزد قدموں کے طور پر واپس آتے ہیں، اس لیے سیٹ اپ مکمل کرنے کی اسکرین اُنہیں یہ جانے بغیر دکھا دیتی ہے کہ فراہم کنندہ کون سا ہے۔ اور برج جو بھی ہدایت لکھتا ہے وہ فراہم کنندہ کا نام اور وہ کمانڈ لکھتا ہے جو ایپ دراصل استعمال کرتی ہے، اس لیے بدلی ہوئی کمانڈ یا دوسرا فراہم کنندہ بغیر کسی نئی اسٹرنگ کے درست پڑھا جاتا ہے۔

ورک اسپیس کے چیٹ ٹوکن اُسی ورک اسپیس کی اپنی ڈائریکٹری میں، اُس کے ڈیٹا بیس کے پہلو میں رہتے ہیں — سادہ JSON کے طور پر <dataDir>/<workspaceId>/chat_credentials/<provider>.json پر، ڈیٹا بیس میں نہیں اور دانستہ طور پر OS keychain میں بھی نہیں۔ دو نتائج بعد میں کھو جانا آسان ہے: ورک اسپیس حذف کرنے سے ڈائریکٹری اَن لنک ہو جاتی ہے اور اُس کے ٹوکن اُس کے ساتھ چلے جاتے ہیں، اور ورک اسپیس ایکسپورٹ یا بیک اپ کرنے پر workspace.db نقل ہوتی ہے، اس لیے کسی اور کو دیا گیا ورک اسپیس اپنے اندر زندہ بوٹ ٹوکن ساتھ نہیں لے جاتا۔ فائل سرور ہوسٹ کی فائل سسٹم اعتماد کی حد وراثت میں پاتی ہے — ڈائریکٹری تک پڑھنے کی رسائی رکھنے والا کوئی بھی بوٹ ٹوکن پڑھ سکتا ہے، جو وہی حد ہے جس کے پیچھے ڈیٹا بیس بھی ہیں۔

فی فراہم کنندہ ایک فائل، جوہری طور پر اور صرف مالک کے لیے لکھی جاتی ہے: منسوخ شدہ ایپ ایک فائل اَن لنک کرنے سے حذف ہو جاتی ہے اور خراب فائل سب کے بجائے ایک فراہم کنندہ کا نقصان کرتی ہے۔ اسناد کنیکٹ آپریشن کے ذریعے اندر آتی ہیں اور کبھی واپس باہر نہیں آتیں — اسٹیٹس پڑھنے پر صرف کنکشن میٹا ڈیٹا کا جواب ملتا ہے۔

  • ایپ کی ساختی ترتیبات۔ ٹرانسپورٹ، scopes اور ایونٹ سبسکرپشنز لازم ٹھہرائے جاتے ہیں، ظاہر نہیں کیے جاتے۔ ایسی ایپ جسے ترتیبات کی اسکرین سے خاموشی میں کنفیگر کیا جا سکے وہ فیچر نہیں، سپورٹ ٹکٹ ہے۔
  • گفتگو کے ساتھ سلیش کمانڈز۔ Slack کی سطح پیغامات، تھریڈ جوابات اور سلیش کمانڈز ہیں۔
  • دوسرا ماخذِ سچ۔ چیٹ وہ جگہ نہیں جہاں کام محفوظ ہوتا ہے۔ Slack تھریڈ ایک اسپیس ہے، جواب ایک ٹرانسکرپٹ ہے، ٹکٹ ایک ٹکٹ ہے — سب ورک اسپیس میں، ہر کلائنٹ سے قابلِ رسائی۔