Hoppa till innehåll

Chattbrygga

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.

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

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.

Behörighetsomfång för bottoken:

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

Händelseprenumerationer för boten:

app_home_opened
app_mention
message.channels
message.groups
message.im

Strukturella 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.

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.

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.

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.

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.

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 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.

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.

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

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.

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.

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.

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