Chattbrygga
Leverantörer
Avsnitt med rubriken “Leverantörer”| Leverantör | Trådvärde | Transport | Yttyp |
|---|---|---|---|
| Slack | slack |
Socket Mode (utgående WSS) | slack |
Slack är den enda leverantören. ChatProvider är en enum med ett enda värde och cc_server registrerar en plugin. Schemat, varje uppslag och varje unikt index är nycklade på provider, så id:n korsar aldrig leverantörsgränser och att lägga till en är en ändring som bara rör servern.
En app på leverantörssidan per arbetsyta × leverantör.
Chattkommandon
Avsnitt med rubriken “Chattkommandon”Kommandonamnet är per app (cc som standard, alltså /cc). En administratör kan byta namn på det; bryggan använder därefter det namn den senast anropades med.
| Kommando | Effekt |
|---|---|
/cc help (eller /cc utan verb) |
Listar vad boten kan göra |
/cc link <code> |
Löser in en engångskod och länkar avsändarens chattidentitet |
/cc ticket <title> |
Skapar ett lokalt ärende som rapporterats av den länkade medlemmen |
/cc ticket <title> | <description> |
Samma sak, med allt efter det första | som beskrivning |
| Okänt verb | Rapporterar det okända verbet plus hjälptexten |
Sätt att nå en agent:
| Gest | Beteende |
|---|---|
@bot … i en kanal |
Öppnar en Slack-tråd och en matchande yta i Control Center; agenten svarar i Slack-tråden |
| Svar i en bryggad Slack-tråd | Fortsätter samma yta i Control Center — ingen nämning behövs |
| Vanligt meddelande i en kanal | Ignoreras (riktar sig inte till boten och finns inte i en bryggad Slack-tråd) |
| DM till boten | En yta i Control Center per DM-konversation; sätter en titel på Slack-tråden och en ”is thinking…”-status medan agenten arbetar |
Fält för inloggningsuppgifter (Slack)
Avsnitt med rubriken “Fält för inloggningsuppgifter (Slack)”Deklareras av leverantörsbeskrivningen, som är det som genererar anslutningsdialogen och validerar inlämningen.
| Fält-id | Etikett | Krävs | Prefix | Var den finns |
|---|---|---|---|---|
botToken |
Bot-token | Ja | xoxb- |
OAuth & Permissions |
appToken |
Appnivå-token | Ja | xapp- |
Basic Information → App-Level Tokens, behörighetsomfånget connections:write |
configRefreshToken |
Refresh-token för appkonfiguration | Nej | xoxe |
Appkonfigurationstokens på api.slack.com/apps |
configRefreshToken är inloggningsuppgiften för apphantering: utan den fungerar anslutningen, men boten kan inte skapas eller redigeras från Control Center. Den roteras vid varje användning och det roterade värdet lagras direkt.
Värden trimmas, odeklarerade nycklar tas bort och ett prefix som inte stämmer avvisas med samma mening på klienten och på servern. Krävda fält måste finnas vid varje anslutning; ett valfritt fält som lämnas tomt behåller det som redan var sparat.
Krav på Slack-appen
Avsnitt med rubriken “Krav på Slack-appen”Behörighetsomfång för bottoken:
app_mentions:readassistant:writechannels:historychat:writecommandsgroups:historyim:historyusers:readusers:read.emailHändelseprenumerationer för boten:
app_home_openedapp_mentionmessage.channelsmessage.groupsmessage.imStrukturella inställningar som Control Center framtvingar när den skapar eller redigerar appen: Socket Mode aktiverat, ingen request-URL, fliken Messages aktiverad och skrivbar, boten always_online, de behörighetsomfång och händelser som krävs sammanfogade med det appen redan har (aldrig borttagna) och ett slash-kommando med beskrivningen File a ticket or link your Control Center account — markören som identifierar Control Centers eget kommando i en exporterad manifestfil.
Anslutningstillstånd
Avsnitt med rubriken “Anslutningstillstånd”| Tillstånd | Trådvärde | Betydelse |
|---|---|---|
| Inte ansluten | disconnected |
Inga inloggningsuppgifter sparade för den här arbetsytan × leverantören |
| Ansluter… | connecting |
Inloggningsuppgifterna sparade, transporten kopplar upp eller kopplar upp igen |
| Live | connected |
Transporten är uppe och tar emot händelser |
| Anslutningsfel | error |
Sparade men misslyckas; leverantörens eget fel visas vid sidan om |
Återanslutning sker med exponentiell backoff och jitter, med ett tak på 30 sekunder. En socketrotation som leverantören initierar återansluter snabbt utan att backoffen trappas upp. En avvisad inloggningsuppgift (invalid_auth, not_authed, token_revoked, account_inactive, token_expired, invalid_app_id) eller ett saknat behörighetsomfång är terminalt: bryggan slutar försöka igen tills nya inloggningsuppgifter kommer in.
Leverantörens förmågor (Slack)
Avsnitt med rubriken “Leverantörens förmågor (Slack)”Bryggan är skriven mot den rikaste ytan och degraderar per förmåga.
| Förmåga | Slack | Effekt när den saknas |
|---|---|---|
streaming |
ja | Hela svaret publiceras en gång, på slutet |
streamingRequiresThread |
ja | Ett svarsmål utan trådankare publicerar hela svar |
ephemeralMessages |
ja | Ett svar på ett slash-kommando utan användbart svarshandtag tappas (avslag publiceras alltid i tråden) |
threadStatus |
ja | Ingen tillfällig rad med ”is thinking…” |
threadTitle |
ja | Chattråden behåller sin standardtitel |
slashCommands |
ja | Ingen /cc-yta |
taskCards |
ja | Endast text — inget kort ovanför svaret och ingen länk av typen ”View in Control Center” |
maxMessageLength |
39 000 | Längre svar trunkeras med en hänvisning till Control Center |
maxStreamChunkLength |
3 800 | Tillägg delas vid en stycke-, rad- eller blankstegsgräns |
Att streaming annonseras är inte samma sak som att streaming utlovas: Slacks variant kräver funktionen Agents & AI Apps på en betald plan. Ett avslag vid anropstillfället (streaming_not_supported, feature_not_enabled, paid_only, …) kommer ihåg under anslutningens livstid och rapporteras som streamingAvailable: false. Slacks uppgiftskort följer med i samma anrop, så en Slack som avvisar kortets visningsläge behåller sin ström och förlorar bara kortet, en gång, under anslutningens livstid.
En ström måste också ange vilken läsare den är till för. Slack kräver både en mottagande användare och dennes team för att öppna en i en kanal (ett DM räcker inte) och avvisar annars med missing_recipient_user_id / missing_recipient_team_id — ett avslag per anrop, så bryggan fortsätter försöka och turen kommer under tiden fram som ett publicerat svar. Mottagaren är den som frågade, hämtad från det inkommande meddelandet (dennes team, inte appens, så att en Slack Connect-kanal löses upp korrekt). För en tur som ingen startade från chatten — arbete som dragits igång i Control Center i en redan bryggad konversation — är det medlemmen vars länk skapade bryggan; om hen sedan dess har avlänkat sitt chattkonto publiceras den turen i sin helhet i stället för att strömmas.
Uppgiftskort
Avsnitt med rubriken “Uppgiftskort”Ett grupperat kort per agenttur (task_display_mode: plan), med en rad per steg. Varje rad är en task_update av id + title + status, plus en engångs-details när tanken är färdig. details och output med samma id sammanfogas utan avgränsare, så details skickas en gång och output strömmas aldrig. Planens titel är begäran; svaret är ett separat, senare tillägg som bär en markdown_text-chunk.
| Del | Innehåll |
|---|---|
| Planens titel | Vad som efterfrågades |
| Förberedelserad | Working on it…, sedan Cloning …, sedan Starting the agent… — en rad, titeln ersätts |
| Tankeraden | Thinking…, läggs till när agenten börjar resonera; tanken är den här radens details, skickad en gång när resonemanget är klart |
| Verktygsrader | En per verktyg (Bash which swift), unikt id så att de ligger sida vid sida |
| Status | Pågår medan den raden kör; klar när nästa rad startar eller turen avslutas; fel för misslyckade, avbrutna och turer som nått sin gräns |
| Uppmaning | View in Control Center som en avslutande rad när turen avslutas — saknas medan arbetet fortfarande kör och saknas när servern inte har någon nåbar publik URL |
Vad ett kort aldrig bär: verktygsutdata, ett argument utanför den tillåtna listan eller något som lästs från databasen. Resonemangsprosan är bara Thinking…-radens detaljer (begränsad till 256 tecken), inte meddelandets brödtext. Ett kort som inte skulle säga något nytt skickas inte igen. En nämning öppnar strömmen direkt så att Slack-tråden inte är tyst; en tur som sedan inte producerade någon text alls avslutar samma kort med en kort not i stället för att publicera ett andra meddelande. En ordlös tur i en yta som aldrig öppnade någon ström (inga kort, eller streaming redan avvisad) publicerar fortfarande ett meddelande med det färdiga kortet och noten.
Strömmade och publicerade kort är olika nyttolaster:
| Form | Utformning | Prosa |
|---|---|---|
| Strömmad | plan_update plus task_update-chunks nycklade på id |
Vanlig sträng, begränsad till 256 tecken per chunk |
| Publicerad | Ett plan-block som omsluter task_card:n nycklade på task_id |
Rik text |
Felhantering:
| Situation | Beteende |
|---|---|
| Leverantören avvisar kortet vid ett tillägg | Tillägget görs omedelbart om utan kortet; prosan kommer fortfarande fram live och kort släpps under anslutningens livstid |
| Leverantören avvisar även prosan | Strömmen stängs och svaret publiceras i sin helhet |
| Strömmen bryts efter att en del av svaret har landat | Strömmen stängs och inget publiceras igen |
Se Chattbryggor för varför nyttolasten delas upp på det här sättet.
Förberedelse, innan det finns någon tur
Avsnitt med rubriken “Förberedelse, innan det finns någon tur”Kortet öppnas när en nämning accepteras, före kloningen och före den första token; turen som följer övertar samma kort och ström. Det som sägs därefter kommer från SpaceProvisioningChanged på domänhändelsebussen — samma framsteg som skrivbordsappens förberedelsebanderoll läser av från ytans rad.
| Förberedelseläge | Vad som sägs |
|---|---|
| Nämning accepterad | Working on it… |
Steget repo |
Cloning <repo>… |
Steget prCheckout |
Checking out the pull request in <repo>… |
Steget script |
Running the setup script for <repo>… |
Steget agent |
Setting up <agent>… |
provisioning, inget steg än |
Preparing the workspace… |
ready |
Starting the agent… |
| Agenten resonerar | Thinking… |
failed |
Workspace setup failed — details are in Control Center. |
cancelled |
Workspace setup was stopped — details are in Control Center. |
Det som sägs under förberedelsen kräver alla fyra villkoren:
| Villkor |
|---|
| Leverantören renderar kort |
| Streaming är tillgängligt på den här anslutningen |
| Ytan har en chattbegäran i omlopp |
| Inget kort rapporterar redan en tur där |
Begäran ligger kvar i omlopp tills en tur startar eller förberedelsen misslyckas. En ready-händelse utan ett redan öppet kort är en varm yta och lämnas tyst, och en förberedelse som ingen i chatten väntar på — en konversation som öppnats i appen, en övergiven som återupptas vid start — producerar inget kort alls.
Vidarebefordringssidan /open
Avsnitt med rubriken “Vidarebefordringssidan /open”Chattprodukter accepterar http(s) i en länk och inget annat, så en uppmaning kan inte namnge skrivbordsappens schema direkt.
| Egenskap | Värde |
|---|---|
| Rutt | GET /open/workspaces/<workspaceId>/(spaces|tickets)/<id> |
| Ursprung | Härleds från serverns publika URL (ws → http, wss → https); loopback behålls |
| Svar | En liten HTML-sida med no-store som navigerar till control-center://workspaces/<workspaceId>/(spaces|tickets)/<id> och visar en manuell knapp |
| Validering | Id:n måste matcha [A-Za-z0-9_.-]{1,128}; allt annat ger 404 |
| Åtkomst | Publik och inert — den läser ingen databas och är därför inget existensorakel; appen den öppnar sköter auktoriseringen |
Fält för botprofilen
Avsnitt med rubriken “Fält för botprofilen”| Fält | Gräns | Kommentarer |
|---|---|---|
appName |
1–35 tecken | Standardvärdet är Control Center · <workspace name> |
botDisplayName |
1–80 tecken | Standardvärdet är control-center |
description |
≤ 140 tecken | |
agentDescription |
≤ 300 tecken | Visas ovanför ett DM med boten |
command |
≤ 32 tecken | Normaliseras: inledande snedstreck tas bort, små bokstäver, [^a-z0-9_-] tas bort, tomt värde faller tillbaka på cc |
agentEnabled |
— | Om DM-/agentupplevelsen exponeras |
En tom ruta betyder ”låt den vara”, inte ”rensa den”. Gränserna är Slacks, och de är de snävaste bland de produkter som modelleras.
Länkkoder
Avsnitt med rubriken “Länkkoder”| Egenskap | Värde |
|---|---|
| Längd | 6 tecken |
| Alfabet | 23456789ABCDEFGHJKLMNPQRSTVWXYZ (inga 0/O, 1/I, U) |
| Livstid | 15 minuter |
| Användningar | En |
| Lagring | I minnet — en serveromstart ogiltigförklarar utestående koder |
| Omfattning | En arbetsyta och en leverantör; att skapa en ny kod ogiltigförklarar medlemmens föregående |
Inlösen matchas skiftlägesokänsligt. Okända, utgångna koder och koder från en annan arbetsyta eller med fel leverantör misslyckas alla identiskt, med avsikt.
Länkmetoder
Avsnitt med rubriken “Länkmetoder”| Metod | Trådvärde | Hur |
|---|---|---|
| Kod | code |
Medlemmen löste in /cc link <code> |
| E-post | email |
Leverantörens verifierade e-postadress matchade en användare som redan är medlem i arbetsytan |
RPC-operationer
Avsnitt med rubriken “RPC-operationer”Alla gäller en arbetsyta. Servern är tillståndslös, så varje anrop bär sitt eget workspace_id i argumenten; dispatcharen avvisar en operation som gäller en arbetsyta men utelämnar det, innan någon databas öppnas. Inloggningsuppgifter färdas bara in — statusläsningar returnerar dem aldrig.
| Operation | Typ | Minsta roll | Åtgärdsklasser |
|---|---|---|---|
chat.providers |
read | guest |
— |
chat.status |
read | guest |
— |
chat.connect |
mutate | admin |
secretAccess, networkEgress |
chat.disconnect |
destructive | admin |
secretAccess |
chat.listUserLinks |
read | guest |
— |
chat.beginUserLink |
mutate | member |
— |
chat.unlinkUser |
mutate | member för dig själv, admin för alla andra |
— |
chat.createApp |
mutate | admin |
secretAccess, networkEgress |
chat.setupLink |
read | admin |
— |
chat.botProfile |
read | admin |
networkEgress |
chat.updateBotProfile |
mutate | admin |
networkEgress |
Läsoperationer ärver guest-golvet från sin typ; mutationer utan ett uttalat golv kräver member.
chat.setupLink sätter samman en URL för att skapa en leverantörsapp utifrån botprofilen. Den deklarerar ingen åtgärdsklass eftersom den varken läser någon hemlighet eller ringer något — det är användarens egen webbläsare som gör det enda anropet.
En reaktiv fråga kompletterar ytan:
| Fråga | Typ | Sänder ut |
|---|---|---|
chat.watchUserLinks |
watch | Listan, som skickas på nytt varje gång en länk skapas eller tas bort |
Den finns för att länkningen sker på andra sidan: en medlem skriver in engångskoden i boten, så inget klientsvar kan bära tillbaka svaret. Listan i inställningarna och den öppna dialogen ”länka mitt konto” följer båda den, och dialogen växlar till en bekräftelse i samma stund som medlemmens egen länk landar.
chat.beginUserLink genererar alltid en kod för anroparens egen identitet. chat.updateBotProfile returnerar en remaining_step bara när ett steg återstår (vanligtvis en ominstallation); att den saknas betyder att ändringen är aktiv.
chat.providers returnerar en post per erbjuden leverantör — beskrivningen plus, när det finns en, anslutningsstatusen. Allt som inställningsgränssnittet renderar kommer från den nyttolasten, vilket är varför att lägga till en leverantör är en ändring som bara rör servern.
| Vad | Var |
|---|---|
| Anslutningens inloggningsuppgifter | <dataDir>/<workspaceId>/chat_credentials/<provider>.json |
| Halvfärdig guidad konfiguration | <dataDir>/<workspaceId>/chat_credentials/<provider>_setup.json |
| Äldre Slack-filer | <dataDir>/<workspaceId>/slack_credentials.json och slack_app_setup.json, läses en gång och migreras på plats |
Inloggningsuppgifterna ligger bredvid arbetsytans databas, inte inuti den: att radera en arbetsyta tar med sig dess chattokens, och workspace.export eller en säkerhetskopia kopierar workspace.db — så en arbetsyta som lämnas till någon annan bär inga levande bottokens. Skrivningarna är atomiska (tempfil plus namnbyte) och stramas åt till endast ägaren.
Databastabeller
Avsnitt med rubriken “Databastabeller”Båda ligger i arbetsytans databas, som bär en ihopslagen v1-baslinje — så en läsning mellan arbetsytor kompilerar inte.
chat_space_links — en extern konversation ↔ en yta i Control Center:
| Kolumn | Kommentarer |
|---|---|
id |
UUID v4 |
workspace_id |
Arbetsytans omfattning |
provider |
ChatProvider.wire |
external_team_id |
Slack-team T…, tomt för leverantörer som inte har begreppet |
external_channel_id |
Slack-kanal C…, grupp G…, DM D… |
external_thread_id |
Slack-trådankare, eller null när hela konversationen är ankaret (bot-DM) |
cc_space_id |
REFERENCES spaces(id) ON DELETE CASCADE |
created_by_user_id, created_at, last_activity_at |
Unikt på (workspace_id, provider, external_channel_id, external_thread_id), med ett partiellt index som upprätthåller samma sak för null-tråd-id:n (SQLite behandlar NULL som distinkta).
chat_user_links — en extern medlem ↔ en Control Center-användare:
| Kolumn | Kommentarer |
|---|---|
id |
UUID v4 |
workspace_id |
Arbetsytans omfattning |
provider |
ChatProvider.wire |
external_team_id, external_user_id |
Slack-medlem U… |
user_id |
Control Center-användare (global, alltså ingen främmande nyckel) |
method |
code eller email |
linked_at |
Unikt på (workspace_id, provider, external_team_id, external_user_id) och på (workspace_id, provider, user_id) — envärdig tillskrivning i båda riktningarna, samtidigt som en person kan vara länkad hos flera leverantörer.
En länk är identitetsuppslagning, inte en åtkomstbeviljning: medlemskapet kontrolleras på nytt vid varje inkommande händelse, så en länk till en medlem som sedan dess tagits bort leder till ett avslag.
Yttyp och meddelandemetadata
Avsnitt med rubriken “Yttyp och meddelandemetadata”En bryggad yta skapas med leverantörens SpaceKind (slack). Den beter sig som en vanlig människovänd yta — aviserad och sektionerad som topic — och isExternalChat är den kontroll som varje beteende (spegling, märkning, bevarande) går genom.
Varje meddelande som bryggan tar in bär en metadata['chat']-stämpel:
{ "chat": { "provider": "slack", "teamId": "T024BE7LD", "channelId": "C0G9QF9GW", "threadId": "1700000000.000100", "messageId": "1700000000.000200", "userId": "U024BE7LH", "userLabel": "sam" }}Stämpeln är också ekovakten: den utgående speglingen hoppar över varje meddelande som bär den, vilket är det som hindrar ett inkommande chattmeddelande från att publiceras rakt tillbaka.
Vidarebefordran
Avsnitt med rubriken “Vidarebefordran”| Beteende | Detalj |
|---|---|
| Vad som vidarebefordras ut | Endast agentens text, plus uppgiftskortets statusrad. Resonemangsprosa och verktygsutdata stannar i Control Center |
| Streamingfrekvens | Strypts till en flush per sekund medan en tur körs; kortet och texten det förklarar färdas i samma anrop |
| Mänskliga meddelanden som skrivs i Control Center | Speglas till den länkade tråden, med författarens visningsnamn som prefix |
| Tomma turer | Rapporteras som en kort not (körningen misslyckades, avbröts eller stoppades vid sin tur-gräns) i stället för tystnad |
| Deduplicering | Nycklas på leverantörens eget händelse-id; de senaste 512 kommer ihåg |
| Ingångsagent | En ny bryggad konversation väcker arbetsytans VD-agent, annars dess första agent, annars ingen |
| Ytnamn | #channel · <request> för en Slack-kanaltråd, Slack DM · <request> för ett DM, titlar kapade vid 48 tecken |
| Felisolering | En dålig händelse tar aldrig ner bryggan; anslutningen fortsätter betjäna alla andra konversationer |
- Konfigurera Slack-integration
- Länka ditt Slack-konto
- Anpassa chattboten
- Chattbryggor
- Räcken för de åtgärdsklasser som operationerna deklarerar