Langkau ke kandungan

Jambatan sembang

Jambatan sembang menjadikan produk sembang luaran — Slack buat masa ini — cara kelas pertama untuk masuk ke ruang kerja Control Center. Sebut bot dan ejen akan terjaga; balas dalam utas Slack dan perbualan berterusan; taip perintah slash dan satu tiket difailkan. Di sebaliknya, ruang, transkrip, tiket dan atribusi yang sama seperti yang digunakan oleh aplikasi desktop. Sembang ialah permukaan pada ruang kerja, bukan salinannya.

Slack ialah satu-satunya penyedia yang disediakan. Tiada pelaksanaan Discord, Telegram, Teams atau Matrix di mana-mana dalam produk ini. Ciri ini sengaja ditulis secara bebas penyedia — menambah produk kedua hanya memerlukan satu nilai enum, satu penyesuai dan satu plugin — tetapi hari ini “sembang” bermaksud Slack.

Dua permukaan merangkumi ciri ini dan kedua-duanya milik orang yang berbeza. Menyambungkan ruang kerja ke Slack dan menyesuaikan bot ialah tindakan pentadbir di bawah Tetapan → Ruang kerja → Umum. Memautkan akaun sembang anda sendiri kepada identiti Control Center anda ialah tindakan ahli di bawah Tetapan → Ruang kerja → Profil & identiti.

Tiga keputusan membentuk keseluruhan ciri ini: ke arah mana sambungan itu berjalan, apa yang membenarkan mesej yang tiba dari luar dan di mana pengetahuan khusus produk dibenarkan berada.

Jambatan ini tidak pernah mendengar daripada penyedia. Slack dicapai melalui Socket Mode: pelayan meminta URL soket daripada Slack dan menghubungi keluar, kemudian menerima sebutan, DM dan perintah slash melalui sambungan itu.

Kesannya itulah yang penting. Komputer riba di belakang NAT, pelayan rumah tanpa domain, mesin yang berpindah antara rangkaian — semuanya boleh menjalankan jambatan tanpa port masuk, tanpa terowong, tanpa sijil TLS dan tanpa URL awam yang perlu diganti apabila ia berubah. Tiada titik akhir webhook untuk diamankan kerana memang tiada titik akhir webhook. Apabila Control Center menyusun definisi apl, ia menetapkan Socket Mode dan membuang sebarang URL permintaan, kerana apl yang dikonfigurasikan untuk kedua-duanya ialah apl yang hanya berfungsi separuh.

Kosnya ialah sambungan yang perlu dikekalkan hidup, dan itu tugas pelayan: penyambungan semula menggunakan backoff eksponen dengan jitter yang dihadkan pada 30 saat, putaran soket yang dimulakan oleh penyedia akan menyambung semula tanpa meningkatkan backoff tersebut dan kelayakan yang ditolak bersifat muktamad — mencuba semula token yang telah dibatalkan selama-lamanya hanya akan mendatangkan had kadar, jadi jambatan berhenti dan menunggu token baharu sambil melaporkan ralat penyedia itu sendiri secara verbatim dalam tetapan.

Ruang kerja tanpa apl sembang tidak menelan kos sama sekali: tiada soket, tiada pemasa, tiada trafik. Dan keseluruhannya bermula selepas sepanduk sedia pelayan, kerana apl sembang yang lambat menjawab tidak boleh sesekali melengahkan boot.

Keahlian yang membenarkan, bukan pemilikan token

Bahagian bertajuk “Keahlian yang membenarkan, bukan pemilikan token”

Orang yang memasang apl bukanlah orang yang menghantar mesej. Jadi setiap peristiwa masuk diselesaikan kepada Principal sebelum apa-apa berlaku dan ia gagal secara tertutup dengan penjelasan:

  • Akaun sembang yang belum dipautkan diberitahu cara memautkannya, dalam utas Slack tempat ia menulis.
  • Akaun berpaut yang penggunanya bukan ahli ruang kerja ini diberitahu supaya meminta jemputan daripada pentadbir.
  • Ahli yang peranannya hanya baca (viewer, guest) diberitahu bahawa peranannya tidak boleh memulakan kerja.

Tiada satu pun daripada kes itu diteruskan. Mesej sembang tidak pernah diatribusikan kepada seseorang yang tidak menghantarnya — inilah yang mengekalkan jejak audit, had kadar per Principal dan pelapor tiket supaya jujur. Lihat Multipemain — identiti, keahlian dan kehadiran untuk model keahlian yang menjadi sandarannya.

Memautkan ialah penyelesaian identiti, bukan pemberian akses. Keahlian diperiksa semula pada setiap peristiwa, jadi pautan milik seseorang yang sejak itu telah dibuang akan menghasilkan penolakan dan bukan keistimewaan lamanya. Dua laluan mewujudkan pautan: kod sekali guna yang berumur pendek yang ditaip ahli dalam sembang, atau padanan automatik pada e-mel disahkan penyedia — dan laluan e-mel masih memerlukan pengguna yang dipadankan sudah menjadi ahli, jadi orang asing yang alamatnya kebetulan wujud pada pelayan tidak dipautkan kepada apa-apa.

Kod-kod itu sengaja disimpan dalam memori, dengan masa hayat 15 minit. Menyimpannya secara kekal akan mencipta baris berbentuk kelayakan yang tahan lama untuk sesuatu yang memang bertujuan menguap, dan but semula pelayan yang membatalkan setiap kod yang masih belum digunakan ialah tingkah laku yang betul, bukan regresi — ahli hanya perlu menekan “pautkan” sekali lagi.

Penolakan dijawab dalam utas Slack tempat mesej itu dibuka, sebagai balasan sebenar. Balasan berutas memberi lencana kepada mesej yang dijawabnya, sampai kepada satu-satunya orang yang memerlukannya dan masih mengekalkan “anda belum dipautkan” di luar saluran itu sendiri. Menyiarkan mesej juga satu-satunya keupayaan yang dimiliki oleh setiap penyedia, jadi penjelasan itu tidak pernah bergantung pada ciri yang mungkin tidak dibawa oleh apl.

Jambatan yang mencerminkan kedua-dua arah akan berulang tanpa henti melainkan ada sesuatu yang membezakan kedua-dua arah itu. Setiap mesej yang dibawa masuk oleh jambatan dicap dengan metadata['chat'] — penyedia, pasukan, perbualan, utas, mesej dan penghantar. Cermin keluar melangkau mana-mana mesej yang membawa cap itu.

Peraturan tunggal itulah yang menjadikan pencerminan dua hala selamat: mesej manusia yang ditaip dalam Control Center disalurkan keluar ke utas berpaut (diawali dengan nama penulis), manakala mesej yang datang dari sembang berhenti di tempat ia masuk.

Apa yang melintas ke luar sengaja dibataskan. Jawapan ejen disalurkan sebagai teks sembang biasa. Fikiran pendek boleh muncul pada baris Thinking… pada kad itu. Keluaran alat ialah longgokan diff dan kekal dalam Control Center, yang juga menjadi tempat balasan yang dipotong menunjuk.

Di tempat penyedia merendernya, satu giliran membawa satu kad tugas, yang disunting di tempat selama giliran itu berjalan dan terletak di atas jawapan yang dijelaskannya.

Kad itu ialah satu pelan yang dikumpulkan, bukan perenggan yang dilecek. Slack menggabungkan details dan output pada tugas yang sama, jadi butiran sesuatu baris dihantar sekali sahaja (fikiran pada Thinking…) dan output tidak pernah dialirkan. Persediaan ialah satu baris yang tajuknya diganti (Working on it…, Cloning …). Penaakulan menambah baris Thinking…. Setiap alat mempunyai barisnya sendiri (Read lib/main.dart). Jawapan ialah teks biasa di bawah kad — paparan pelan Slack membacanya daripada lampiran markdown yang datang kemudian, bukan daripada cebisan yang dicampur ke dalam tugasan. Tiada apa-apa lagi: tiada keluaran alat dan hanya argumen alat daripada senarai izin yang pendek, kerana argumen alat yang tidak dikenali boleh menjadi keseluruhan isi fail manakala satu baris hanyalah satu baris.

Kad itu juga merangkumi masa menunggu sebelum giliran. Sebutan pertama mencipta perbualan dan penciptaannya mengklon setiap repo yang akan digunakan oleh ejen untuk bekerja — yang pada repo sebenar mengambil masa beberapa minit ketika ejen belum diminta apa-apa lagi dan utas itu kelihatan seperti bot mengabaikan permintaan tersebut. Jadi kad itu terbuka sebaik sahaja sebutan diterima (Working on it…) dan kemudian baris persediaan itu dikemas kini dengan perkataan yang sama seperti yang ditunjukkan oleh desktop — Cloning acme/widgets…, Setting up Reviewer…, Starting the agent… — kemudian baris Thinking… sebaik sahaja ejen mula menaakul, kemudian satu baris bagi setiap alat. Satu kad, satu mesej, daripada “saya mendengar anda” sehingga jawapan sebagai teks biasa di bawahnya. Persediaan hanya dilaporkan apabila ada seseorang dalam sembang yang menunggunya: perbualan yang sedang disediakan dalam apl, atau perbualan terkandas yang disambung semula semasa boot pelayan, menyediakan sumber tanpa kad sama sekali.

Ia membawa satu-satunya perkara yang tidak dapat diberikan oleh sembang kepada pembaca: pautan View in Control Center ke perbualan di sebalik balasan itu. Produk sembang hanya menerima http(s) dalam pautan dan tidak menerima selainnya, jadi butang itu menunjuk kepada halaman kecil yang dilayani oleh pelayan ini, yang melantunkan pelayar ke pautan control-center:// aplikasi desktop. Halaman itu tidak membaca apa-apa dan tidak menyelesaikan apa-apa — id dalam URL-nya digaungkan ke pautan mendalam dan tidak ke mana-mana lagi — dan pelayan tanpa URL awam yang boleh dicapai hanya menghasilkan kad tanpa butang dan bukan kad yang rosak.

Kad ialah satu keupayaan seperti keupayaan lain. Penyedia yang tidak memilikinya berkelakuan tepat seperti sebelum ini, teks sahaja, dan penyedia yang menolak satu kad pada masa panggilan mengekalkan alirannya: balasan sentiasa mengatasi kad yang menerangkannya. Apabila penstriman dimatikan sepenuhnya, kad yang telah siap menaiki satu-satunya mesej yang disiarkan sebaliknya. Tiket yang difailkan mendapat layanan yang sama — pengesahan /cc ticket ialah kad yang telah siap yang sebab kewujudannya ialah pautan ke tiket yang baru dicipta.

Produk sembang berbeza dari segi apa yang boleh direndernya, jadi jambatan ini ditulis untuk permukaan terkaya — balasan yang membesar secara langsung di dalam utas, status sementara “is thinking…”, utas bertajuk, jawapan seketika kepada perintah slash — dan menurunkan satu keupayaan pada satu masa. Penyedia yang tidak memiliki satu pun daripadanya masih berfungsi; balasan hanya tiba sebagai satu mesej.

Mengiklankan sesuatu keupayaan bukan bermakna menjanjikannya. Balasan penstriman Slack memerlukan ciri pelan berbayar dan Slack hanya menolaknya pada masa panggilan. Penolakan yang ditandakan kekal diingati sepanjang seluruh hayat sambungan itu: jambatan berhenti meminta dan menyiarkan balasan penuh selepas itu dan tetapan menyatakannya secara terang, kerana “balasan saya berhenti menstrim” harus boleh dijawab tanpa membaca log. Kad tugas digugurkan dengan cara yang sama jika Slack menolak satu.

Setiap had milik penyedia dan bukan milik relai: had siling mesej, had siling cebisan strim dan sama ada sesuatu strim memerlukan sauh utas semuanya diisytiharkan oleh penyesuai, dan balasan panjang dipecah pada sempadan perenggan, baris atau perkataan dan bukan di tengah perkataan.

Satu jambatan, satu penyesuai bagi setiap produk

Bahagian bertajuk “Satu jambatan, satu penyesuai bagi setiap produk”

Segala yang berbentuk produk berada di sebalik port. Teras generik mengetahui tentang markdown, perbualan sembang, utas sembang dan ahli; ia tidak mengetahui apa-apa tentang sampul Slack, mrkdwn atau intent gerbang. Tiga bahagian menjadikannya berkesan:

  • Penyesuai menormalkan satu produk: pengangkutannya, panggilan API-nya, kodek teksnya dan keanehan sampulnya. Milik Slack memberi pengajaran — sebutan saluran tiba dua kali (sebagai app_mention dan sebagai message) dan kebanyakan subjenis mesej hanyalah hiasan dan bukan permintaan baharu. Kedua-duanya diselesaikan dalam penyesuai, jadi teras tidak pernah tahu bahawa Slack menghantar dua kali.
  • Penerang ialah bentuk awam penyedia: kelayakan apa yang diperlukannya, apa formatnya, URL mana yang membantu pengguna menemuinya, apa yang boleh dilakukannya dan sama ada aplnya boleh dicipta atau disunting dari sini. Kad tetapan, dialog sambungan dan pengesahan argumen semuanya dijana daripadanya — klien menghantar balik penyedia serta peta yang dikunci oleh id medan.
  • Plugin ialah gubahan di sisi pelayan: bagaimana kelayakan menjadi sambungan yang disahkan, bagaimana penyesuai dibina dan bagaimana apl di sisi penyedia diuruskan.

Jadi menambah produk ialah satu nilai enum, satu penyesuai dan satu plugin. Tiada op RPC baharu, tiada perubahan skema, tiada perubahan klien — dan klien kekal jujur, kerana medan yang tidak pernah didengarnya masih dirender dengan label yang dibekalkan oleh pelayan.

Dua butiran menghalang abstraksi itu daripada meresap ke atas. Langkah pengurusan apl yang tidak mempunyai API pada penyedia (jana token, pasang apl, pasang semula selepas perubahan kebenaran) dikembalikan sebagai langkah bernama dengan pautannya sendiri, supaya skrin selesai persediaan boleh memaparkannya tanpa tahu penyedia mana itu. Dan setiap arahan yang ditulis oleh jambatan menyebut penyedia dan perintah yang sebenarnya digunakan oleh apl, supaya perintah yang dinamakan semula atau penyedia kedua terbaca dengan betul tanpa rentetan baharu.

Token sembang sesuatu ruang kerja berada dalam direktori ruang kerja itu sendiri, di sebelah pangkalan datanya — sebagai JSON biasa di <dataDir>/<workspaceId>/chat_credentials/<provider>.json, bukan dalam pangkalan data dan sengaja bukan dalam keychain OS. Dua kesannya mudah dilupakan kemudian hari: memadam ruang kerja akan menyahpaut direktori itu dan tokennya turut hilang, dan mengeksport atau menyandarkan ruang kerja menyalin workspace.db, jadi ruang kerja yang diserahkan kepada orang lain tidak membawa token bot yang masih aktif di dalamnya. Fail itu mewarisi sempadan kepercayaan sistem fail hos pelayan — apa-apa yang mempunyai akses baca ke direktori data boleh membaca token bot, dan itulah sempadan yang sama yang melindungi pangkalan data.

Satu fail bagi setiap penyedia, ditulis secara atomik dan hanya untuk pemilik: apl yang dibatalkan dipadam dengan menyahpaut satu fail dan fail yang rosak hanya merugikan satu penyedia dan bukan kesemuanya. Kelayakan masuk ke dalam melalui operasi sambungan dan tidak pernah keluar semula — bacaan status hanya menjawab dengan metadata sambungan.

  • Tetapan apl yang bersifat struktur. Pengangkutan, skop dan langganan peristiwa ditetapkan dan bukan didedahkan. Apl yang boleh dikonfigurasikan sehingga membisu daripada skrin tetapan ialah tiket sokongan dan bukan ciri.
  • Perbualan serta perintah slash. Permukaan Slack ialah mesej, balasan utas dan perintah slash.
  • Sumber kebenaran kedua. Sembang bukan tempat kerja disimpan. Utas Slack ialah ruang, balasannya ialah transkrip, tiketnya ialah tiket — semuanya dalam ruang kerja, boleh dicapai daripada setiap klien.