Integrations
The desktop app's Integrations tab - the same backend-owned connections the web app makes, connected in-app with any provider sign-in opening in the system browser.
The Integrations tab is the desktop screen onto the same backend-owned catalog the web app uses - one catalog, one set of connections, one place they are stored. A user who connects a service in their browser finds it already connected in the app, and the reverse. AI integrations documents the system; this page is what the app adds.
| Declared in | packages/config/src/ai/integrations.ts - see Catalog entries |
| Where users connect | Settings > Integrations |
| What it offers | every catalog entry whose surfaces include desktop |
| Stored | encrypted in your backend database, per user - nothing on the device |
| Shown when | config.ai.enabled, inside the desktop agents envelope |
The tab is gated on the AI service, never on how full the catalog is: every input to the tab row is a build fact, so no tab waits on a network read. An empty catalog renders the empty state rather than hiding the tab - it is where a user connects their first integration, so hiding it until they had would be a door that only opens once you are through it.
What the app adds
- Sign-in opens the system browser. An
oauthentry sends the user to the provider in their real browser, so their existing session and password manager work, and the app never handles the credential. - No local credential store. There is no keychain entry and no on-device dialog: the app calls the same
/ai/integrationsroutes the web app does, over its device-granted bearer token. - Connections follow the workspace, not the device. Each is placed at a scope - a project, an organization, or the connector alone - from the same picker the web app shows, and a second machine inherits whatever that scope makes visible at sign-in. One connection is a tool on every surface at once: web chat, automated runs, and this app's own agents.
How a sign-in reaches the browser
The app authenticates with a bearer token, and a freshly opened system browser carries no session cookie for your API - so it cannot simply open the session-guarded /start URL. The app asks the backend for a start handoff instead: a short-lived, single-use token minted over the channel that is authenticated, then spent by the browser.
POST /api/ai/integrations/oauth/{provider}/handoff, authenticated, with the placement it wants: { "scope": "project" | "organization" | "user", "projectId": "..." } (both optional; omitted scope means the connector alone).{ "startUrl": "...", "expiresInSeconds": 60 }.startUrl in the system browser, verbatim. The URL is built server-side, so the app never assembles a path or a parameter name.| Property | Value |
|---|---|
| Lifetime | 60 seconds |
| Uses | one - a replayed link starts nothing |
| Bound to | the minting user, that one provider, and the placement resolved at mint time |
| Can it be traded for a session? | No. It begins one authorization and does nothing else. |
The token stands in for a session in a URL handed to the OS, so it will be written to browser history. That is why it is single-use and expires in a minute - and why the account the tokens land on comes from the server-side record, never from anything the browser sends. A leaked link cannot connect an integration to anyone but the person who minted it.
Local MCP servers are a different feature
An mcp integration is a streamable-HTTP server your backend dials. A stdio server runs on the user's own machine, where no backend can reach it: users add those to their own coding CLI, the app leaves them alone, and they never appear on this tab.
Shared automations
A definition one member writes and every member can choose to run on their own machine - what it stores, how turning it on works, and why consent is the only gate on it.
Agent runtime
The agent runtime a desktop app with AI forks from its own binary - consent, lifecycle, and what runs on-device.