From 5014fd31d30e93d29bbe731e047c9e950d5a48db Mon Sep 17 00:00:00 2001 From: Greg Holmes Date: Tue, 2 Dec 2025 09:55:52 +0000 Subject: [PATCH 1/4] Add AI transport as a product within docs --- src/data/index.ts | 14 +++++++++++++- src/data/languages/languageData.ts | 3 +++ src/data/nav/aitransport.ts | 23 +++++++++++++++++++++++ src/data/nav/index.ts | 11 ++++++++++- src/data/types.ts | 2 +- src/pages/docs/ai-transport/index.mdx | 6 ++++++ 6 files changed, 56 insertions(+), 3 deletions(-) create mode 100644 src/data/nav/aitransport.ts create mode 100644 src/pages/docs/ai-transport/index.mdx diff --git a/src/data/index.ts b/src/data/index.ts index b7bd28c49a..c36331ceb1 100644 --- a/src/data/index.ts +++ b/src/data/index.ts @@ -1,4 +1,12 @@ -import { chatNavData, liveObjectsNavData, liveSyncNavData, platformNavData, pubsubNavData, spacesNavData } from './nav'; +import { + aiTransportNavData, + chatNavData, + liveObjectsNavData, + liveSyncNavData, + platformNavData, + pubsubNavData, + spacesNavData, +} from './nav'; import { languageData } from './languages'; import { PageData, ProductData } from './types'; import homepageContentData from './content/homepage'; @@ -16,6 +24,10 @@ export const productData = { nav: chatNavData, languages: languageData.chat, }, + aiTransport: { + nav: aiTransportNavData, + languages: languageData.aiTransport, + }, spaces: { nav: spacesNavData, languages: languageData.spaces, diff --git a/src/data/languages/languageData.ts b/src/data/languages/languageData.ts index 24bebd829c..83992637e8 100644 --- a/src/data/languages/languageData.ts +++ b/src/data/languages/languageData.ts @@ -29,6 +29,9 @@ export default { swift: '1.0', kotlin: '1.0', }, + aiTransport: { + javascript: '2.11', + }, spaces: { javascript: '0.4', react: '0.4', diff --git a/src/data/nav/aitransport.ts b/src/data/nav/aitransport.ts new file mode 100644 index 0000000000..53699c56e5 --- /dev/null +++ b/src/data/nav/aitransport.ts @@ -0,0 +1,23 @@ +import { NavProduct } from './types'; + +export default { + name: 'Ably AI Transport', + link: '/docs/ai-transport', + icon: { + closed: 'icon-gui-prod-ai-transport-outline', + open: 'icon-gui-prod-ai-transport-solid', + }, + content: [ + { + name: 'Introduction', + pages: [ + { + name: 'About AI Transport', + link: '/docs/ai-transport', + index: true, + }, + ], + }, + ], + api: [], +} satisfies NavProduct; diff --git a/src/data/nav/index.ts b/src/data/nav/index.ts index aac3975f22..e5ed49fddb 100644 --- a/src/data/nav/index.ts +++ b/src/data/nav/index.ts @@ -1,8 +1,17 @@ import platformNavData from './platform'; import pubsubNavData from './pubsub'; import chatNavData from './chat'; +import aiTransportNavData from './aitransport'; import liveObjectsNavData from './liveobjects'; import spacesNavData from './spaces'; import liveSyncNavData from './livesync'; -export { platformNavData, pubsubNavData, chatNavData, liveObjectsNavData, spacesNavData, liveSyncNavData }; +export { + platformNavData, + pubsubNavData, + chatNavData, + aiTransportNavData, + liveObjectsNavData, + spacesNavData, + liveSyncNavData, +}; diff --git a/src/data/types.ts b/src/data/types.ts index 0884a04a8b..a9c2b2977b 100644 --- a/src/data/types.ts +++ b/src/data/types.ts @@ -3,7 +3,7 @@ import { LanguageData } from './languages/types'; import { NavProduct } from './nav/types'; const pageKeys = ['homepage'] as const; -const productKeys = ['platform', 'pubsub', 'chat', 'spaces', 'liveObjects', 'liveSync'] as const; +const productKeys = ['platform', 'pubsub', 'chat', 'aiTransport', 'spaces', 'liveObjects', 'liveSync'] as const; export type ProductKey = (typeof productKeys)[number]; type PageKey = (typeof pageKeys)[number]; diff --git a/src/pages/docs/ai-transport/index.mdx b/src/pages/docs/ai-transport/index.mdx new file mode 100644 index 0000000000..fb2f2b271e --- /dev/null +++ b/src/pages/docs/ai-transport/index.mdx @@ -0,0 +1,6 @@ +--- +title: About AI Transport +meta_description: "Learn more about Ably's AI Transport and the features that enable you to quickly build functionality into new and existing applications." +redirect_from: + - /docs/products/ai-transport +--- From 229953cf1d7768844ec0ae41bb72051612b46ad1 Mon Sep 17 00:00:00 2001 From: Mike Christensen Date: Wed, 10 Dec 2025 23:42:31 +0000 Subject: [PATCH 2/4] ai-transport/sessions-identity: add overview Adds an overview page for a Sessions & Identity section which describes the channel-oriented session model and its benefits over the traditional connection-oriented model. --- src/data/nav/aitransport.ts | 9 +++ .../ai-transport/ait-http-streaming.webp | Bin 0 -> 24628 bytes .../content/ai-transport/ait-with-ait.webp | Bin 0 -> 49388 bytes .../sessions-identity/overview.mdx | 54 ++++++++++++++++++ 4 files changed, 63 insertions(+) create mode 100644 src/images/content/ai-transport/ait-http-streaming.webp create mode 100644 src/images/content/ai-transport/ait-with-ait.webp create mode 100644 src/pages/docs/ai-transport/sessions-identity/overview.mdx diff --git a/src/data/nav/aitransport.ts b/src/data/nav/aitransport.ts index 53699c56e5..86aeaa0c56 100644 --- a/src/data/nav/aitransport.ts +++ b/src/data/nav/aitransport.ts @@ -18,6 +18,15 @@ export default { }, ], }, + { + name: 'Sessions & Identity', + pages: [ + { + name: 'Overview', + link: '/docs/ai-transport/sessions-identity/overview', + }, + ], + }, ], api: [], } satisfies NavProduct; diff --git a/src/images/content/ai-transport/ait-http-streaming.webp b/src/images/content/ai-transport/ait-http-streaming.webp new file mode 100644 index 0000000000000000000000000000000000000000..e48fd9754c544bb328c7dc7c9d1d00356a084688 GIT binary patch literal 24628 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 Z7dtO6)N>#j{IjTx?~KM4t;7F+{12r81DyZ> literal 0 HcmV?d00001 diff --git a/src/images/content/ai-transport/ait-with-ait.webp b/src/images/content/ai-transport/ait-with-ait.webp new file mode 100644 index 0000000000000000000000000000000000000000..410b032608dc63e4a41c322569a3a16ebc59310a GIT binary patch literal 49388 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 Date: Thu, 11 Dec 2025 10:13:56 +0000 Subject: [PATCH 3/4] ai-transport/sessions-identity: add identity Describes how identity relates to session management and how this works in the context of channel-oriented sessions. --- .../ai-transport/sessions-identity/overview.mdx | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/src/pages/docs/ai-transport/sessions-identity/overview.mdx b/src/pages/docs/ai-transport/sessions-identity/overview.mdx index 396b06f475..836be8eee2 100644 --- a/src/pages/docs/ai-transport/sessions-identity/overview.mdx +++ b/src/pages/docs/ai-transport/sessions-identity/overview.mdx @@ -3,7 +3,7 @@ title: "Sessions & identity overview" meta_description: "Manage session lifecycle and identity in decoupled AI architectures" --- -When building AI applications with Ably AI Transport, session management works differently than in traditional HTTP streaming architectures. Sessions persist beyond individual connections, enabling agents and clients to connect independently through shared channels. +Ably AI Transport provides robust session management and identity capabilities designed for modern AI applications. Sessions persist beyond individual connections, enabling agents and clients to connect independently through shared channels. Built-in token-based authentication provides verified user identity and fine-grained authorization for channel operations. ## What is a session? @@ -52,3 +52,14 @@ The following table compares how each architecture addresses the engineering cha | **Message resume** | Agents must buffer sent messages and implement replay logic. When clients reconnect, agents must determine what was missed and retransmit without duplicates or gaps, distinctly for each connection. | When clients reconnect, they automatically receive messages published while disconnected. The channel maintains history without agents implementing buffering or replay logic, eliminating the need for server-side session state. | | **Abandonment detection** | Agents must implement logic to distinguish between brief network interruptions and users who have actually left, so they can decide whether to continue work or clean up resources. | Built-in presence tracking signals when users enter and leave channels, providing clear lifecycle events to agents without custom detection logic. | | **Multi-user and multi-device** | Agents must manage multiple concurrent connections from the same user across devices, or from multiple users in collaborative sessions. This requires tracking connections, synchronizing state, and ensuring all participants receive consistent updates. | Multiple users and devices can connect to the same channel. The channel handles message delivery to all participants, simplifying agent logic for multi-user and multi-device scenarios. | + +## Identity in channel-oriented sessions + +In connection-oriented architectures, the agent server handles authentication directly when establishing the connection. When the connection is opened, the server verifies credentials and associates the authenticated user identity with that specific connection. + +In channel-oriented sessions, agents don't manage connections or handle authentication directly. Instead, your server authenticates users and issues tokens that control their access to channels. Ably enforces these authorization rules and provides verified identity information to agents, giving you powerful capabilities for managing who can participate in sessions and what they can do: + +- **Verified identity**: Agents automatically receive the authenticated identity of message senders, with cryptographic guarantees that identities cannot be spoofed +- **Granular authorization**: Control precisely what operations each user can perform on specific channels through fine-grained capabilities +- **Rich user attributes**: Pass authenticated user data to agents for personalized behavior without building custom token systems +- **Role-based participation**: Distinguish between different types of participants, such as users and agents, to customize behaviour based on their role From ecfd91f33e5b33d0331ead480a7707564e9c4a63 Mon Sep 17 00:00:00 2001 From: Greg Holmes Date: Mon, 15 Dec 2025 13:26:41 +0000 Subject: [PATCH 4/4] Add session abandonment documentation for AI Transport - Explain abandonment scenarios (tab close, device switch, network loss, idle, app background) - Document presence-based detection with multi-device tracking - Cover four agent response strategies: - Immediate termination for expensive streaming - Graceful completion for conversational AI - Background continuation for long-running tasks - Cost optimization with model switching - Include re-attachment handling with rewind and history for client catch-up --- src/data/nav/aitransport.ts | 4 + .../sessions-identity/session-abandonment.mdx | 434 ++++++++++++++++++ 2 files changed, 438 insertions(+) create mode 100644 src/pages/docs/ai-transport/sessions-identity/session-abandonment.mdx diff --git a/src/data/nav/aitransport.ts b/src/data/nav/aitransport.ts index 86aeaa0c56..87d90936ca 100644 --- a/src/data/nav/aitransport.ts +++ b/src/data/nav/aitransport.ts @@ -25,6 +25,10 @@ export default { name: 'Overview', link: '/docs/ai-transport/sessions-identity/overview', }, + { + name: 'Session abandonment', + link: '/docs/ai-transport/sessions-identity/session-abandonment', + }, ], }, ], diff --git a/src/pages/docs/ai-transport/sessions-identity/session-abandonment.mdx b/src/pages/docs/ai-transport/sessions-identity/session-abandonment.mdx new file mode 100644 index 0000000000..3b6890c136 --- /dev/null +++ b/src/pages/docs/ai-transport/sessions-identity/session-abandonment.mdx @@ -0,0 +1,434 @@ +--- +title: Session abandonment +meta_description: "Detect when users leave AI sessions and respond with appropriate agent strategies." +--- + +Session abandonment occurs when users disconnect from an AI session. This can happen in several ways: + +- **Intentional exit**: User closes the browser tab, navigates away, or explicitly ends the session. +- **Device switch**: User moves from desktop to mobile, leaving the original session behind. +- **Network interruption**: Connection drops due to poor connectivity, VPN changes, or ISP issues. +- **Idle timeout**: User walks away from their device without closing the session. +- **App in background**: On mobile, the app is suspended or terminated by the operating system. + +Each scenario presents different challenges. A user switching devices may return on the new device within seconds. A network interruption might resolve in moments, or indicate the user is gone for hours. An idle user might still be watching the screen, even if not actively interacting. + +In AI Transport's Pub/Sub channel-oriented architecture, agents run independently from client connections. This decoupling means agents must actively detect when users leave and decide how to respond - whether to stop expensive token generation immediately, complete the current response gracefully, continue background processing, or switch to a cheaper model. The right strategy depends on the cost of the operation, the value of partial results, and the likelihood the user will return. + +[Presence](/docs/presence-occupancy/presence) provides the mechanism for detecting user disconnections. When users connect to a session, they enter the channel's presence set. When they disconnect - whether intentionally or due to network issues - they automatically leave the presence set after a brief timeout. Agents subscribed to presence events receive `enter` and `leave` events they can use to trigger appropriate response strategies. + +## Detecting abandonment with presence + +Users signal their participation in a session by entering the channel's [presence](/docs/presence-occupancy/presence) set. When they disconnect - whether intentionally or due to network issues - they automatically leave the presence set, and agents subscribed to presence events receive a `leave` event. + +### Subscribe to presence events + +Agents subscribe to presence events on the session channel to detect when users enter or leave: + + +```javascript +const channel = realtime.channels.get('conversation:{{USER_ID}}'); + +await channel.presence.subscribe((member) => { + switch (member.action) { + case 'leave': + console.log(`User ${member.clientId} left the session`); + // Trigger abandonment handling logic + break; + case 'enter': + console.log(`User ${member.clientId} joined the session`); + break; + } +}); +``` + + +### Tracking multi-device presence + +Users may be connected from multiple devices simultaneously. A `leave` event from one device doesn't necessarily mean the user has abandoned the session - they may still be connected from another device. + +To determine when a user is completely offline, count the number of presence entries for that user's `clientId`: + + +```javascript +const channel = realtime.channels.get('conversation:{{USER_ID}}'); + +async function isUserCompletelyOffline(clientId) { + const members = await channel.presence.get(); + const userConnections = members.filter(m => m.clientId === clientId); + return userConnections.length === 0; +} + +await channel.presence.subscribe('leave', async (member) => { + const offline = await isUserCompletelyOffline(member.clientId); + if (offline) { + console.log(`User ${member.clientId} is completely offline`); + // Handle full abandonment + } else { + console.log(`User ${member.clientId} still connected on another device`); + } +}); +``` + + + + +## Agent response strategies + +How an agent responds to session abandonment depends on the nature of the work being performed. The following strategies cover common scenarios, from immediately stopping expensive operations to continuing background work. + +### Immediate termination + +For expensive streaming operations where there's no value in continuing without an active user, stop generation immediately to save costs. + +This strategy is appropriate for: + +- Token streaming where costs accumulate per token +- Realtime transcription or translation +- Any operation where partial results have no value + + +```javascript +const channel = realtime.channels.get('conversation:{{USER_ID}}'); + +let abortController = null; + +// Start a streaming response +async function startStreaming(prompt) { + abortController = new AbortController(); + + try { + const stream = await aiModel.stream(prompt, { + signal: abortController.signal + }); + + for await (const token of stream) { + channel.publish('token', token); + } + } catch (err) { + if (err.name === 'AbortError') { + // Stream was intentionally stopped + channel.publish('status', { + type: 'terminated', + reason: 'user_left' + }); + } else { + throw err; + } + } +} + +// Stop streaming when user leaves +await channel.presence.subscribe('leave', async (member) => { + const offline = await isUserCompletelyOffline(member.clientId); + if (offline && abortController) { + abortController.abort(); + abortController = null; + } +}); +``` + + +### Graceful completion + +For conversational AI, it often makes sense to complete the current response before stopping. This ensures the conversation ends at a natural point rather than mid-sentence. + +This strategy is appropriate for: + +- Chat applications where partial responses are confusing +- Assistants generating structured outputs that need to be complete +- Any scenario where you want clean conversation boundaries + + +```javascript +const channel = realtime.channels.get('conversation:{{USER_ID}}'); + +let userPresent = true; +let isGenerating = false; + +// Check user presence before starting new responses +async function generateResponse(prompt) { + if (!userPresent) { + console.log('User not present, skipping response'); + return; + } + + isGenerating = true; + + try { + const stream = await aiModel.stream(prompt); + + for await (const token of stream) { + channel.publish('token', token); + } + + // Response complete - check if we should continue + if (!userPresent) { + channel.publish('status', { + type: 'paused', + reason: 'user_left', + message: 'Session paused after completing response' + }); + await cleanup(); + } + } finally { + isGenerating = false; + } +} + +// Track user presence +await channel.presence.subscribe('leave', async (member) => { + const offline = await isUserCompletelyOffline(member.clientId); + if (offline) { + userPresent = false; + // If not currently generating, clean up immediately + if (!isGenerating) { + await cleanup(); + } + // Otherwise, let current generation complete + } +}); + +await channel.presence.subscribe('enter', (member) => { + userPresent = true; +}); + +async function cleanup() { + // Release resources, close connections, etc. + console.log('Session cleaned up'); +} +``` + + +### Background continuation + +For long-running tasks like code generation, document analysis, or data processing, the agent may continue working even after the user disconnects. The user can be notified when results are ready. + +This strategy is appropriate for: + +- Code generation or refactoring tasks +- Document summarization or analysis +- Any task where results are valuable even if delivered later + + +```javascript +const channel = realtime.channels.get('conversation:{{USER_ID}}'); + +let backgroundMode = false; + +async function processTask(task) { + channel.publish('status', { type: 'processing', task: task.id }); + + const result = await performLongRunningWork(task); + + if (backgroundMode) { + // User left during processing - store result and notify + await storeResultForLater(task.userId, result); + channel.publish('status', { + type: 'completed_background', + task: task.id, + message: 'Results ready when you return' + }); + + // Optionally send push notification + await sendPushNotification(task.userId, 'Your task is complete'); + } else { + // User still present - deliver result directly + channel.publish('result', result); + } +} + +await channel.presence.subscribe('leave', async (member) => { + const offline = await isUserCompletelyOffline(member.clientId); + if (offline) { + backgroundMode = true; + channel.publish('status', { + type: 'continuing_background', + message: 'Work will continue in background' + }); + } +}); + +await channel.presence.subscribe('enter', (member) => { + backgroundMode = false; +}); +``` + + +### Cost optimization + +When users disconnect, switch to a more cost-effective model to complete ongoing work. This balances resource usage against the value of completion. + +This strategy is appropriate for: + +- Long responses where the user may return +- Background tasks where latency is less important +- Any scenario where you want to balance cost against completion + + +```javascript +const channel = realtime.channels.get('conversation:{{USER_ID}}'); + +let currentModel = 'fast-expensive-model'; +let userPresent = true; + +async function generateResponse(prompt, conversationHistory) { + const model = userPresent ? 'fast-expensive-model' : 'slow-cheap-model'; + + if (model !== currentModel) { + currentModel = model; + channel.publish('status', { + type: 'model_switched', + model: currentModel, + reason: userPresent ? 'user_returned' : 'user_away' + }); + } + + const stream = await aiModel.stream(prompt, { + model: currentModel, + history: conversationHistory + }); + + for await (const token of stream) { + // Check if model should change mid-stream + if (userPresent && currentModel === 'slow-cheap-model') { + // User returned - could restart with faster model + // or continue with current for consistency + } + channel.publish('token', token); + } +} + +await channel.presence.subscribe('leave', async (member) => { + const offline = await isUserCompletelyOffline(member.clientId); + if (offline) { + userPresent = false; + } +}); + +await channel.presence.subscribe('enter', (member) => { + userPresent = true; +}); +``` + + +## Re-attachment handling + +When users return to a session after disconnecting, agents need to restore state and clients need to catch up on any missed content. + +### Agent detects user return + +When a user re-enters the presence set, the agent can resume normal operation: + + +```javascript +const channel = realtime.channels.get('conversation:{{USER_ID}}'); + +let sessionState = { + mode: 'active', + model: 'fast-model', + pendingResults: [] +}; + +await channel.presence.subscribe('enter', async (member) => { + console.log(`User ${member.clientId} returned`); + + // Restore active mode + sessionState.mode = 'active'; + sessionState.model = 'fast-model'; + + // Notify user of any work completed while away + if (sessionState.pendingResults.length > 0) { + channel.publish('status', { + type: 'resumed', + completedWhileAway: sessionState.pendingResults.length + }); + + // Deliver pending results + for (const result of sessionState.pendingResults) { + channel.publish('result', result); + } + sessionState.pendingResults = []; + } else { + channel.publish('status', { type: 'resumed' }); + } +}); + +await channel.presence.subscribe('leave', async (member) => { + const offline = await isUserCompletelyOffline(member.clientId); + if (offline) { + sessionState.mode = 'background'; + sessionState.model = 'slow-cheap-model'; + } +}); +``` + + +### Client catches up on missed tokens + +When a client reconnects, it can use [rewind](/docs/channels/options/rewind) to retrieve messages published while it was disconnected: + + +```javascript +// Client reconnecting to a session +const channel = realtime.channels.get('conversation:{{USER_ID}}', { + params: { rewind: '2m' } // Retrieve last 2 minutes of messages +}); + +// Track which responses we've already seen +const seenResponses = new Set(loadFromLocalStorage('seenResponses')); + +await channel.subscribe((message) => { + if (message.name === 'token') { + const responseId = message.extras?.headers?.responseId; + + // Skip tokens from responses we already have + if (seenResponses.has(responseId)) { + return; + } + + appendToken(message.data, responseId); + } else if (message.name === 'status') { + handleStatusUpdate(message.data); + } +}); + +// Enter presence to signal we're back +await channel.presence.enter({ device: 'web', reconnected: true }); +``` + + +For longer disconnection periods, use [history](/docs/storage-history/history) with persistence enabled to retrieve older messages: + + +```javascript +// Client recovering after longer disconnection +const channel = realtime.channels.get('persisted:conversation:{{USER_ID}}'); + +// Subscribe to live messages first +await channel.subscribe((message) => { + handleMessage(message); +}); + +// Then fetch history to catch up +let page = await channel.history({ untilAttach: true }); + +while (page) { + // Process historical messages (newest first) + for (const message of page.items) { + handleHistoricalMessage(message); + } + + page = page.hasNext() ? await page.next() : null; +} + +// Signal we're back +await channel.presence.enter(); +``` + + +