Use this file to discover all available pages before exploring further.
Triggered before a conversation thread is loaded from history. Use this event to inspect or modify the messages before they are displayed in the chat UI.
A dedicated namespace for your custom metadata. Use this namespace to:
Prevent conflicts with system properties.
Keep your custom data organized.
Ensure future platform updates do not break your code.
To use this namespace, create a new message_state object and a new user_defined object inside it. Add your custom properties inside user_defined.
If message_state or user_defined was previously sealed or made non-extensible, directly assigning a new nested property can throw an Object is not extensible error in strict mode. To make your code more robust and preserve existing properties, use the spread operator to create a fresh object reference before adding custom fields:
Array of message IDs to persist to the backend database.When you modify historical messages in pre:threadLoaded, those changes only affect what displays in the UI. To save your modifications to the backend, add the messageId of each modified message to this array. This triggers automatic PATCH API calls that persist your changes.
The system does not automatically set tracking properties. If you need to track whether modifications were applied, create a new message_state object and set the flag inside message_state.user_defined before adding the message ID to the array.
The following example demonstrates how to modify historical messages when loading a conversation thread, including adding feedback controls to agent responses and updating message text:
instance.on('pre:threadLoaded', (event, instance) => { // Log all messages being loaded from conversation history console.log('Loading thread with messages:', event.messages); // Add feedback controls to all agent responses in the conversation history // This allows users to provide feedback on past agent responses event?.messages.forEach((message) => { if (message?.sender === 'response') { // Get the first content item const [lastItem] = message.content; lastItem.message_options = { feedback: { is_on: true // Enable feedback controls for this message } }; // Initialize feedback state with default values // This prepares the message to accept user feedback message.message_state = { content: { 1: { feedback: { text: "", // Empty feedback text initially is_positive: true, // Default to positive feedback selected_categories: [] // No categories selected yet } } } }; } }); // Modify the first message in the conversation // This could be used to add a welcome back message or context if (event.messages.length > 0) { event.messages[0].content[0].text = 'Previous conversation loaded'; }});
The following example demonstrates how to translate all historical messages when loading conversation history, with intelligent skipping of already-translated content:
instance.on('pre:threadLoaded', async (event, instance) => { // Define the user's preferred language const userLanguage = 'es'; // User's preferred language const messageIdsToUpdate = []; // Track which messages need to be persisted // Only process messages if the user's language is not English if (userLanguage !== 'en') { for (const message of event.messages) { // Skip user messages - only translate agent responses // User messages are already in the user's language if (message.sender === 'user') { continue; } // Skip if already translated to current language // This prevents unnecessary re-translation and API calls if (message.message_state?.user_defined?.translated_language === userLanguage) { console.log(`Skipping message ${message.messageId} - already translated`); continue; } // Skip English originals when user language is English // No translation needed for English content if (message.message_state?.user_defined?.translated_language === 'en') { console.log(`Skipping message ${message.messageId} - English original`); continue; } // Translate each text content item in the message // Messages can contain multiple content items (text, images, etc.) for (const content of (message.content || [])) { if (content.response_type === 'text' && content.text) { // Translate from English to user's language // Note: translateText() is a placeholder - implement your own translation service const translatedText = await translateText(content.text, 'en', userLanguage); content.text = translatedText; } } // Store custom metadata in user_defined namespace // Use the spread operator to preserve existing properties and avoid // issues with sealed or non-extensible objects in strict mode message.message_state = { ...message.message_state, user_defined: { ...(message.message_state?.user_defined || {}), translated_language: userLanguage } }; // Add this message ID to the update list // This will trigger a PATCH API call to persist the translation messageIdsToUpdate.push(message.messageId); } } // Trigger automatic persistence for all modified messages // The system will make PATCH API calls for each message ID in this array event.messageIdsForUpdate = messageIdsToUpdate;});
The following example demonstrates how to optimize translation performance by tracking the current language state of each message and only translating when necessary:
instance.on('pre:threadLoaded', async (event, instance) => { // User switches to French - could happen when user changes language preference const userLanguage = 'fr'; // User switches to French const messageIdsToUpdate = []; // Track messages that need persistence // Only process if user language is not English if (userLanguage !== 'en') { for (const message of event.messages) { // Skip user messages - they're already in the user's language if (message.sender === 'user') continue; // Get the current translation state of this message from user_defined namespace // This tells us what language the message is currently in const currentLanguage = message.message_state?.user_defined?.translated_language; // Skip if already in target language // This is the key optimization - avoid re-translating messages if (currentLanguage === userLanguage) { continue; } // Translate from any language to French // This handles cases where messages were previously translated to other languages for (const content of (message.content || [])) { if (content.response_type === 'text' && content.text) { // Determine source language (default to English if never translated) // This allows translation from Spanish->French, German->French, etc. const sourceLanguage = currentLanguage || 'en'; content.text = await translateText(content.text, sourceLanguage, userLanguage); } } // Store the new translation language in user_defined namespace // This prevents re-translation on next load message.message_state = { ...message.message_state, user_defined: { ...(message.message_state?.user_defined || {}), translated_language: userLanguage } }; messageIdsToUpdate.push(message.messageId); } } // Trigger automatic persistence for all modified messages event.messageIdsForUpdate = messageIdsToUpdate; // Log the number of messages that were translated for debugging console.log(`Translated ${messageIdsToUpdate.length} messages to ${userLanguage}`);});