Přeskočit na obsah

Chatový most

Poskytovatel Wire hodnota Přenos Druh prostoru
Slack slack Socket Mode (odchozí WSS) slack

Jediný poskytovatel je Slack. ChatProvider je výčet s jedinou hodnotou a cc_server registruje jeden plugin. Schéma, každé vyhledání i každý unikátní index jsou klíčované podle provider, takže identifikátory se mezi poskytovateli nekříží a přidání dalšího je změna jen na serveru.

Jedna aplikace na straně poskytovatele na každý pracovní prostor × poskytovatel.

Jméno příkazu patří aplikaci (výchozí je cc, tedy /cc). Admin ho může přejmenovat; most pak používá to jméno, pod kterým byl naposledy zavolán.

Příkaz Účinek
/cc help (nebo /cc bez slovesa) Vypíše, co bot umí
/cc link <code> Uplatní jednorázový kód a propojí chatovou identitu odesílatele
/cc ticket <title> Založí lokální ticket, který nahlásí propojený člen
/cc ticket <title> | <description> Totéž a všechno za prvním | je popis
Neznámé sloveso Nahlásí neznámé sloveso a připojí text nápovědy

Jak se dostat k agentovi:

Gesto Chování
@bot … v kanálu Otevře vlákno Slacku a odpovídající prostor Control Center; agent odpoví ve vláknu Slacku
Odpověď v propojeném vláknu Slacku Pokračuje ve stejném prostoru Control Center — zmínka není potřeba
Obyčejná zpráva v kanálu Ignoruje se (není adresovaná botovi a není v propojeném vláknu Slacku)
Přímá zpráva botovi Jeden prostor Control Center na každou konverzaci v přímé zprávě; nastaví nadpis vlákna Slacku a stav „is thinking…“, dokud agent pracuje

Pole přihlašovacích údajů (Slack)

Sekce “Pole přihlašovacích údajů (Slack)”

Deklaruje je deskriptor poskytovatele. Z něj se generuje dialog připojení a podle něj se kontroluje odeslání.

Id pole Popisek Povinné Prefix Kde leží
botToken Bot user OAuth token Ano xoxb- OAuth & Permissions
appToken App-level token Ano xapp- Basic Information → App-Level Tokens, scope connections:write
configRefreshToken App configuration refresh token Ne xoxe App configuration tokens on api.slack.com/apps

configRefreshToken je přihlašovací údaj pro správu aplikace: bez něj připojení funguje, ale bota z Control Center nejde vytvořit ani upravit. Při každém použití se otočí a otočená hodnota se uloží hned.

Hodnoty se oříznou, nedeklarované klíče se zahodí a nesoulad prefixu se odmítne stejnou větou na klientovi i na serveru. Povinná pole musí být při každém připojení vyplněná; prázdné volitelné pole nechá uloženou hodnotu, jak byla.

Požadavky na aplikaci Slack

Sekce “Požadavky na aplikaci Slack”

Scopes tokenu bota:

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

Odběry událostí bota:

app_home_opened
app_mention
message.channels
message.groups
message.im

Strukturální nastavení, které Control Center vynutí, když aplikaci vytváří nebo upravuje: zapnutý Socket Mode, žádná request URL, Messages tab zapnutá a zapisovatelná, bot always_online, požadované scopes a události sloučené do toho, co aplikace už má (nikdy se neodečítají), a jeden příkaz s lomítkem s popisem File a ticket or link your Control Center account — značka, podle které se v exportovaném manifestu pozná vlastní příkaz Control Center.

Stav Wire Význam
Nepřipojeno disconnected Pro tenhle pracovní prostor × poskytovatel nejsou uložené žádné přihlašovací údaje
Připojování… connecting Přihlašovací údaje jsou uložené, přenos se navazuje nebo navazuje znovu
Živě connected Přenos běží a přijímá události
Chyba připojení error Uložené, ale selhává; vedle se ukáže vlastní chyba poskytovatele

Opětovné připojení je exponenciální backoff s jitterem, nanejvýš 30 sekund. Když socket otočí sám poskytovatel, most se připojí znovu hned a backoff přitom nezvyšuje. Odmítnuté přihlašovací údaje (invalid_auth, not_authed, token_revoked, account_inactive, token_expired, invalid_app_id) nebo chybějící scope jsou konečné: most přestane zkoušet znovu, dokud nepřijdou nové přihlašovací údaje.

Schopnosti poskytovatele (Slack)

Sekce “Schopnosti poskytovatele (Slack)”

Most je napsaný proti nejbohatší ploše a podle chybějící schopnosti ubírá.

Schopnost Slack Účinek, když chybí
streaming ano Celá odpověď se odešle jednou, na konci
streamingRequiresThread ano Cíl odpovědi bez kotvy vlákna posílá celé odpovědi
ephemeralMessages ano Odpověď na příkaz s lomítkem bez použitelného úchytu odpovědi se zahodí (odmítnutí se vždy odešlou do vlákna)
threadStatus ano Žádný pomíjivý řádek „is thinking…“
threadTitle ano Chatové vlákno si nechá výchozí nadpis
slashCommands ano Žádná plocha /cc
taskCards ano Jen text — nad odpovědí není karta a chybí odkaz „View in Control Center“
maxMessageLength 39 000 Delší odpovědi se zkrátí a odkážou do Control Center
maxStreamChunkLength 3 800 Přírůstky se dělí na hranici odstavce, řádku nebo mezery

Ohlášené streamování ještě není slíbené: to Slacku potřebuje funkci Agents & AI Apps na placeném plánu. Odmítnutí v okamžiku volání (streaming_not_supported, feature_not_enabled, paid_only, …) se pamatuje po celou dobu života spojení a navenek je streamingAvailable: false. Karty úkolů Slacku jedou stejným voláním, takže Slack, který odmítne režim zobrazení karty, si stream nechá a o kartu přijde jen jednou, po celou dobu života spojení.

Stream musí taky pojmenovat čtenáře, pro kterého je. Slack v kanálu (v přímé zprávě ne) vyžaduje jak příjemce, tak jeho tým, jinak otevření odmítne s missing_recipient_user_id / missing_recipient_team_id. To je odmítnutí jednoho volání, takže most to zkouší dál a tah mezitím dorazí jako jedna odeslaná odpověď. Příjemce je ten, kdo se ptal, vzatý z příchozí zprávy (jeho tým, ne tým aplikace, takže se kanál Slack Connect rozřeší správně). U tahu, který z chatu nikdo nespustil — práce spuštěná v Control Center na už propojené konverzaci — je to člen, jehož propojení ten most vytvořilo; když si mezitím chatový účet odpojil, ten tah se odešle celý, bez streamování.

Jedna seskupená karta na tah agenta (task_display_mode: plan), s řádkem na každý krok. Každý řádek je task_update z id + title + status, plus jednorázové details, až je myšlenka hotová. details a output se stejným id se spojí bez oddělovače, takže details se pošlou jednou a output se nikdy nestreamuje. Nadpis plánu je požadavek; odpověď je pozdější samostatný přírůstek s kusem markdown_text.

Část Obsah
Nadpis plánu Na co se ptalo
Řádek přípravy Working on it…, pak Cloning …, pak Starting the agent… — jeden řádek, nadpis se nahrazuje
Řádek uvažování Thinking…, přidá se, když agent začne uvažovat; myšlenka jsou details tohoto řádku, odeslané jednou, když uvažování skončí
Řádky nástrojů Jeden na nástroj (Bash which swift), jedinečné id, takže stojí vedle sebe
Stav Probíhá, dokud řádek běží; hotovo, když začne další řádek nebo tah skončí; chyba u selhání, přerušení a limitu tahů
Výzva k akci View in Control Center jako koncový řádek, když tah skončí — chybí, dokud práce ještě běží, a chybí, když server nemá dosažitelnou veřejnou URL

Co karta nikdy nenese: výstup nástroje, argument mimo ten seznam povolených, ani nic přečteného z databáze. Uvažování v próze jsou jen podrobnosti řádku Thinking… (strop 256 znaků), ne tělo zprávy. Karta, která by neřekla nic nového, se neposílá znovu. Zmínka otevře stream hned, aby vlákno Slacku nemlčelo; tah, který pak nevyprodukoval vůbec žádný text, tu samou kartu uzavře krátkou poznámkou, místo aby poslal druhou zprávu. Tah bez textu v prostoru, který stream nikdy neotevřel (žádné karty, nebo streamování už odmítnuté), pořád odešle jednu zprávu s hotovou kartou a poznámkou.

Streamované a odeslané karty jsou jiné payloady:

Podoba Tvar Próza
Streamovaná plan_update plus kusy task_update klíčované id Obyčejný řetězec, strop 256 znaků na kus
Odeslaná Blok plan, který obaluje task_card klíčované task_id Rich text

Zacházení se selháním:

Situace Chování
Poskytovatel odmítne kartu při přírůstku Přírůstek se hned zopakuje bez ní; próza pořád přichází živě a karty se na dobu života spojení zahodí
Poskytovatel odmítne i prózu Stream se zavře a odpověď se odešle celá
Stream se přeruší poté, co část odpovědi dorazila Stream se zavře a nic se neposílá znovu

Viz Chatové mosty, proč se payload dělí právě takhle.

Příprava, než existuje tah

Sekce “Příprava, než existuje tah”

Karta se otevře, když je zmínka přijata, před klonováním a před prvním tokenem; tah, který následuje, převezme stejnou kartu a stejný stream. Další hlášení přichází z SpaceProvisioningChanged na sběrnici doménových událostí — stejný postup, který čte banner přípravy na desktopu z řádku prostoru.

Stav přípravy Hlášení
Zmínka přijata Working on it…
Krok repo Cloning <repo>…
Krok prCheckout Checking out the pull request in <repo>…
Krok script Running the setup script for <repo>…
Krok agent Setting up <agent>…
provisioning, ještě bez kroku Preparing the workspace…
ready Starting the agent…
Agent uvažuje Thinking…
failed Workspace setup failed — details are in Control Center.
cancelled Workspace setup was stopped — details are in Control Center.

Hlášení přípravy potřebuje všechny čtyři podmínky:

Podmínka
Poskytovatel vykresluje karty
Na tomhle spojení je streamování dostupné
V prostoru běží chatový požadavek
Žádná karta tam už tah nehlásí

Požadavek zůstane rozpracovaný, dokud nezačne tah nebo příprava neselže. Událost ready bez už otevřené karty znamená prostor, který už připravený je, a zůstane zticha. Příprava, na kterou v chatu nikdo nečeká — konverzace otevřená v aplikaci, nebo přerušená a obnovená při startu — kartu nevyrobí vůbec.

Odrazová stránka /open

Sekce “Odrazová stránka /open”

Chatové produkty v odkazu přijmou http(s) a nic jiného, takže výzva k akci nemůže pojmenovat schéma desktopu přímo.

Vlastnost Hodnota
Trasa GET /open/workspaces/<workspaceId>/(spaces|tickets)/<id>
Původ Odvozený z veřejné URL serveru (ws → http, wss → https); loopback se zachová
Odpověď Malá HTML stránka no-store, která přejde na control-center://workspaces/<workspaceId>/(spaces|tickets)/<id> a ukáže ruční tlačítko
Kontrola Identifikátory musí odpovídat [A-Za-z0-9_.-]{1,128}; cokoli jiného je 404
Přístup Veřejná a netečná — nečte žádnou databázi, takže neprozradí, jestli záznam existuje; autorizaci udělá aplikace, kterou otevře
Pole Limit Poznámky
appName 1–35 znaků Výchozí Control Center · <workspace name>
botDisplayName 1–80 znaků Výchozí control-center
description ≤ 140 znaků
agentDescription ≤ 300 znaků Zobrazí se nad přímou zprávou s botem
command ≤ 32 znaků Normalizace: úvodní lomítka pryč, malá písmena, [^a-z0-9_-] se odstraní, prázdný výsledek spadne na cc
agentEnabled — Jestli se vystavuje rozhraní přímých zpráv a agenta

Prázdné pole znamená „nech to být“: uložená hodnota zůstane. Limity jsou limity Slacku, nejpřísnější z produktů, které se modelují.

Vlastnost Hodnota
Délka 6 znaků
Abeceda 23456789ABCDEFGHJKLMNPQRSTVWXYZ (bez 0/O, 1/I, U)
Platnost 15 minut
Použití Jedno
Úložiště V paměti — restart serveru zneplatní ještě neuplatněné kódy
Rozsah Jeden pracovní prostor a jeden poskytovatel; vystavení nového kódu zneplatní předchozí kód člena

Uplatnění se páruje bez ohledu na velikost písmen. Neznámý kód, prošlý kód, kód z cizího pracovního prostoru a kód pro špatného poskytovatele selžou schválně stejně.

Způsob Wire Jak
Kód code Člen uplatnil /cc link <code>
E-mail email Ověřený e-mail poskytovatele se shodoval s uživatelem, který už je členem pracovního prostoru

Všechny jsou v rozsahu pracovního prostoru. Server je bezstavový, takže každé volání nese vlastní workspace_id v argumentech; dispatcher odmítne operaci v rozsahu pracovního prostoru, která ho vynechá, ještě než se otevře jakákoli databáze. Přihlašovací údaje putují jen dovnitř — čtení stavu je nikdy nevrátí.

Operace Druh Minimální role Třídy akcí
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 pro sebe, admin pro kohokoli jiného —
chat.createApp mutate admin secretAccess, networkEgress
chat.setupLink read admin —
chat.botProfile read admin networkEgress
chat.updateBotProfile mutate admin networkEgress

Čtecí operace dědí podlahu guest ze svého druhu; mutace bez výslovné podlahy vyžadují member.

chat.setupLink složí URL pro vytvoření aplikace u poskytovatele z profilu bota. Nedeklaruje třídu akce, protože nečte žádné tajemství a nikam nevolá — jediné volání udělá prohlížeč uživatele.

Plochu doplňuje jeden reaktivní dotaz:

Dotaz Druh Vysílá
chat.watchUserLinks watch Seznam, znovu odeslaný, kdykoli se propojení přidá nebo odebere

Existuje proto, že propojení se děje na druhé straně: člen napíše jednorázový kód botovi, takže žádná odpověď klientovi tu zprávu nést nemůže. Seznam v nastavení i otevřený dialog „Propojit účet {provider}“ ho oba sledují a dialog se přepne na potvrzení v okamžiku, kdy dorazí vlastní propojení člena.

chat.beginUserLink vždy vystaví kód pro identitu volajícího. chat.updateBotProfile vrátí remaining_step jen tehdy, když nějaký krok zbývá (typicky nová instalace); když pole chybí, úprava už platí.

chat.providers vrátí jednu položku na nabízeného poskytovatele — deskriptor a, když existuje, stav připojení. Všechno, co vykreslí nastavení, pochází z toho payloadu, a proto je přidání poskytovatele změna jen na serveru.

Co Kde
Přihlašovací údaje spojení <dataDir>/<workspaceId>/chat_credentials/<provider>.json
Nedokončené vedené nastavení <dataDir>/<workspaceId>/chat_credentials/<provider>_setup.json
Starší soubory Slacku <dataDir>/<workspaceId>/slack_credentials.json a slack_app_setup.json, přečtou se jednou a migrují na místě

Přihlašovací údaje leží vedle databáze pracovního prostoru, ne v ní: smazání pracovního prostoru vezme chatové tokeny s sebou a workspace.export nebo záloha kopíruje workspace.db — takže pracovní prostor předaný někomu jinému v sobě nenese živé tokeny bota. Zápisy jsou atomické (dočasný soubor a přejmenování) a práva se stáhnou jen na vlastníka.

Obě žijí v databázi pracovního prostoru, která nese sjednocený baseline v1 — čtení napříč pracovními prostory se proto nezkompiluje.

chat_space_links — jedna externí konverzace ↔ jeden prostor Control Center:

Sloupec Poznámky
id UUID v4
workspace_id Rozsah pracovního prostoru
provider ChatProvider.wire
external_team_id Tým Slacku T…, prázdné u poskytovatelů, kteří ten pojem nemají
external_channel_id Kanál Slacku C…, skupina G…, přímá zpráva D…
external_thread_id Kotva vlákna Slacku, nebo null, když je kotvou celá konverzace (přímé zprávy bota)
cc_space_id REFERENCES spaces(id) ON DELETE CASCADE
created_by_user_id, created_at, last_activity_at

Unikátní na (workspace_id, provider, external_channel_id, external_thread_id), s částečným indexem, který totéž vynutí pro prázdná id vlákna (SQLite bere hodnoty NULL jako různé).

chat_user_links — jeden externí člen ↔ jeden uživatel Control Center:

Sloupec Poznámky
id UUID v4
workspace_id Rozsah pracovního prostoru
provider ChatProvider.wire
external_team_id, external_user_id Člen Slacku U…
user_id Uživatel Control Center (globální, proto bez cizího klíče)
method code nebo email
linked_at

Unikátní na (workspace_id, provider, external_team_id, external_user_id) a na (workspace_id, provider, user_id) — přiřazení je v obou směrech jednoznačné, přičemž jeden člověk může být propojený u několika poskytovatelů.

Propojení je určení identity, ne udělení přístupu: členství se kontroluje znovu při každé příchozí události, takže propojení člena, který byl mezitím odebrán, skončí odmítnutím.

Druh prostoru a metadata zprávy

Sekce “Druh prostoru a metadata zprávy”

Propojený prostor vznikne s SpaceKind poskytovatele (slack). Chová se jako obyčejný prostor pro člověka — oznamovaný a řazený do sekcí jako topic — a isExternalChat je kontrola, kterou projde každé chování (zrcadlení, odznaky, uchovávání).

Každá zpráva, kterou most přinese, nese razítko metadata['chat']:

{
"chat": {
"provider": "slack",
"teamId": "T024BE7LD",
"channelId": "C0G9QF9GW",
"threadId": "1700000000.000100",
"messageId": "1700000000.000200",
"userId": "U024BE7LH",
"userLabel": "sam"
}
}

Razítko je taky ochrana proti ozvěně: odchozí zrcadlo přeskočí každou zprávu, která ho nese, a právě to zabrání tomu, aby se příchozí chatová zpráva poslala rovnou zpátky.

Chování Detail
Co jde ven Jen text agenta a stavový řádek karty úkolu. Próza uvažování a výstup nástroje zůstanou v Control Center
Kadence streamování Při běhu tahu nejvýš jedno odeslání za sekundu; karta a text, který vysvětluje, jedou stejným voláním
Lidské zprávy napsané v Control Center Zrcadlí se do propojeného vlákna s předponou zobrazovaného jména autora
Prázdné tahy Nahlásí se krátkou poznámkou (běh selhal, byl přerušen, nebo zastavil na limitu tahů), ne tichem
Deduplikace Klíčem je vlastní id události poskytovatele; pamatuje se posledních 512
Vstupní agent Nová propojená konverzace probudí agenta CEO pracovního prostoru, jinak jeho prvního agenta, jinak nikoho
Pojmenování prostoru #channel · <request> pro vlákno kanálu Slacku, Slack DM · <request> pro přímou zprávu, nadpisy se stropem 48 znaků
Izolace selhání Jedna špatná událost most neshodí; spojení dál obsluhuje každou další konverzaci