Web Component API
<rtc-agent> is the sole component exposed by RTC Agent. Built on Lit, it contains 57 sub-components and 23 Controllers internally, but presents only a clean Web Component interface externally — attribute configuration, event listening, and CSS variable customization.
Creating the Component
Section titled “Creating the Component”Factory Function (Recommended)
Section titled “Factory Function (Recommended)”Use createRtcAgent() to create a component instance. This is the recommended integration method, providing full type safety and lifecycle management:
import { createRtcAgent, z } from '@rtc-agent/component';import type { RtcAgentConfig, RtcAgentWithLifecycle } from '@rtc-agent/component';
const config: RtcAgentConfig = { // Basic configuration appLabel: 'My AI Assistant', theme: 'system', lang: 'en-US', databaseName: 'my-app-rtc',
// Server configuration server: { url: 'https://rtc-agent.cherish.chat', redirectUri: '/auth/callback.html', },
// SharedWorker URL (required for multi-tab support) workerURL: '/rtc-agent/shared-worker.js',
// Authentication (AuthProvider, see "Auth Configuration" below) auth: { getToken: async () => localStorage.getItem('token') || '', getUserId: () => 'user-123', deviceId: 'uuid-from-your-backend', // required — must match the Device ID in the server-issued JWT },
// Window configuration window: { defaultMode: 'normal', draggable: true, resizable: true, bubblePosition: { corner: 'bottom-right', offset: { x: -24, y: 24 }, }, },
// Activity Bar configuration (hide unnecessary tabs) activityBar: { disabledActivities: ['settings'], },
// Function registration agentName: 'MyApp', agentDescription: 'AI assistant for my application', persona: 'You are a helpful assistant...', groups: [ { name: 'editor', description: 'Editor operations', functions: [ { name: 'getCode', description: 'Get the current code', handler: () => window.editorAPI.getCode(), returns: { zodSchema: z.string().describe('Current code in the editor') }, }, ], }, ],
// Event handlers on: { ready: () => { console.log('RTC Agent is ready'); }, },};
const agent: RtcAgentWithLifecycle = createRtcAgent(config);
// ⚠️ Must be manually appended to DOMdocument.body.appendChild(agent);
// Cleanup when done (removes all event listeners and WebSocket connections)agent.destroy();💡 Important:
createRtcAgent()returns the component instance but does not auto-append to DOM. You must manually calldocument.body.appendChild(agent).
HTML Attributes (Simple Scenarios)
Section titled “HTML Attributes (Simple Scenarios)”For simple scenarios or CDN quick previews, you can use HTML attributes directly:
<script type="module" src="https://cdn.jsdelivr.net/npm/@rtc-agent/component@0.3.0-rc.4/dist/index.js"></script>
<rtc-agent theme="dark" app-label="My AI Assistant"></rtc-agent>⚠️ HTML attributes cannot configure complex object options like
auth. For production, the factory function is recommended for full configuration.
TypeScript Support
Section titled “TypeScript Support”The component package includes .d.ts type declarations — no extra @types package is needed. Once you install @rtc-agent/component, TypeScript recognizes the types automatically.
Main Exported Types
Section titled “Main Exported Types”import type { // Factory function config and return type RtcAgentConfig, // Config object type for createRtcAgent() RtcAgentWithLifecycle, // Return type of createRtcAgent() (includes destroy())
// Auth configuration AuthConfig, AuthProvider,
// Event callbacks and event type map EventCallbacks, RtcAgentEventDetailMap, // DOM event name → detail type mapping
// Window & Activity Bar WindowConfig, ActivityBarConfig, ResolvedActivityBarConfig, Activity, // 'chat' | 'files' | 'functions' | 'settings'
// Agent declarative config AgentConfig, AgentFunctionGroup,
// Data models Session, Message, ConnectionState, // Connection state (for connectionStateChange callback)
// Function registration and validation FunctionDef, FunctionGroupDef, ParameterDef, ReturnDef, OpenAPISchema, VisualHooks, ValidationError, ValidationResult,
// Function Debugger DebugHistoryItem, FunctionDebugState, LogEntry, LogLevel, ExecutionStatus, HistoryPaginationState,
// Scenario documents ScenarioDef, ScenarioManifest, RegistryConfig,} from '@rtc-agent/component';Main Exported Values and Runtime APIs
Section titled “Main Exported Values and Runtime APIs”import { // Factory function createRtcAgent,
// Ready signal whenReady,
// Imperative function registration defineRegistry, FunctionRegistry, FunctionGroup,
// Zod validation utilities z, // Re-export of the Zod library validateParams, formatValidationError, buildValidator, withValidation, zodToParams, zodToOpenAPISchema, openApiToZod, withMeta, // Attach example and other metadata to Zod schemas
// Scenario document loading loadScenariosFromURL, loadScenariosContent, parseFrontmatter,
// Markdown documentation generation generateFunctionMd, generateFunctionsIndex, generateScenariosIndex, generateAgentMd,
// EventBus (advanced — observe internal tool call events) EventBus, eventBus, createEventBus,
// Activity Bar config utilities DEFAULT_ACTIVITY_BAR_CONFIG, resolveActivityBarConfig,
// Internationalization switchLocale, initLocale, getLocale, persistLocale, sourceLocale, targetLocales, localeContext, isValidLocale,
// Theme switchTheme, initTheme, getEffectiveTheme, getStoredTheme, persistTheme,
// Exception class CancelledError, // User cancellation error (throw from hooks.onStart to cancel execution)} from '@rtc-agent/component';Type-Safe Component Access
Section titled “Type-Safe Component Access”Use the RtcAgent type with querySelector to get full property hints:
import type { RtcAgent } from '@rtc-agent/component';
const agent = document.querySelector<RtcAgent>('rtc-agent');
// ✅ Full property and event type hintsagent!.theme = 'dark';agent!.windowConfig = { embedded: true };agent!.addEventListener('rtc-agent-ready', () => { // ...});Event Type Safety
Section titled “Event Type Safety”The component extends HTMLElementEventMap, so addEventListener automatically infers the detail type:
const agent = document.querySelector('rtc-agent')!;
agent.addEventListener('rtc-theme-change', (e) => { // e.detail is automatically inferred as { theme: 'light' | 'dark' | 'system' } console.log(e.detail.theme);});Lifecycle & Cleanup
Section titled “Lifecycle & Cleanup”The destroy() method performs a complete, ordered teardown of the component:
const agent = createRtcAgent(config);document.body.appendChild(agent);
// When the component is no longer needed:agent.destroy();destroy() executes the following steps in order:
- Removes the element from the DOM — calls
this.remove()on the host element, which triggersdisconnectedCallback(clears DOM event listeners, UIUpdateBus subscriptions, RTC processor, auth callbacks, auto-save timers, etc.) - Clears pending auth configuration references — releases factory-captured
_pendingAuthConfig/_pendingDynamicAuth/_pendingAuthProvider - Calls all DOM event unsubscribe functions — every
addEventListenerregistered through the configonfield is removed - Calls all EventBus unsubscribe functions — internal subscriptions (tool call event bridging, etc.) are torn down
💡 When to call: Always call
destroy()before removing the component from the page (e.g., in SPA route changes, modal close, or frameworkunmounthooks). Skipping it leaks event listeners and WebSocket connections.Note:
destroy()does not clear authentication tokens from localStorage (to support Vite HMR — clearing tokens would log the user out on every code change during development). To log out explicitly, callauth.logout().Full integration guide: Integration Tutorial
Auth Configuration
Section titled “Auth Configuration”The auth field in RtcAgentConfig controls how the component obtains authentication credentials. The recommended approach is AuthProvider mode, which delegates all authentication concerns to your provider.
💡 Recommended Approach: For enterprise integration scenarios, Token Exchange mode is strongly recommended. It offers simple integration, great user experience, and eliminates the need to implement a full OAuth2 flow. See Authentication & Authorization - Complete Token Exchange Guide for details.
AuthProvider (Recommended)
Section titled “AuthProvider (Recommended)”Full control over the authentication lifecycle. Implement this when your host application already manages auth state and you want the component to integrate with it.
AuthProvider supports two authentication modes:
| Mode | type value | Recommendation | Use case |
|---|---|---|---|
| Token Exchange (Recommended) | 'token-exchange' | ⭐⭐⭐⭐⭐ | Enterprise integration with existing auth systems |
| OAuth2 Redirect | 'oauth2-redirect' | ⭐⭐⭐ | Standalone apps requiring full login flow |
Token Exchange (Recommended)
Section titled “Token Exchange (Recommended)”When the host application holds an external JWT (e.g., an admin JWT issued by admin-server), the component can exchange it for an RTC main-server JWT via Token Exchange. This is suitable for Admin UI integration scenarios.
const config: RtcAgentConfig = { // ...other config auth: { type: 'token-exchange', getExchangeToken: async () => adminAuthStore.getAdminToken(), isLoggedIn: () => adminAuthStore.isAuthenticated(), logout: async () => { await adminAuthStore.clearSession(); }, getUserId: () => adminAuthStore.getUserId(), deviceId: 'uuid-from-your-backend', },};💡 Token Exchange mode does not require
getToken()orrefreshToken()— the component handles JWT exchange and refresh internally. The main server must configuretoken_exchange.external_issuersinconfig.yamlto trust the corresponding JWT issuer.📚 Complete Integration Guide: See Authentication & Authorization - Complete Token Exchange Integration Guide for architecture, configuration, JWT signing, JWKS endpoints, and more.
OAuth2 Redirect (Alternative)
Section titled “OAuth2 Redirect (Alternative)”const config: RtcAgentConfig = { // ...other config auth: { // type: 'oauth2-redirect', // default, can be omitted getToken: async () => myAuthStore.getAccessToken(), refreshToken: async () => myAuthStore.refreshAccessToken(), isLoggedIn: () => myAuthStore.isAuthenticated, logout: async () => { await myAuthStore.signOut(); }, getUserId: () => myAuthStore.getUserId(), // returns the current user's unique ID deviceId: 'uuid-from-your-backend', // required — must match the Device ID in the server-issued JWT },};💡 PKCE handled automatically: In OAuth2 redirect mode, the component’s built-in login dialog automatically generates PKCE
code_verifier/code_challenge(RFC 7636) — sendingcode_challengein the authorization request andcode_verifierduring token exchange. Developers do not need to handle PKCE parameters manually — PKCE is enabled by default when using the built-in login flow. If the host application manages the OAuth2 flow independently (without the built-in login dialog), it must implement PKCE on its own.
AuthProvider Method Reference
Section titled “AuthProvider Method Reference”| Method | Required | Applicable modes | Description |
|---|---|---|---|
type | Optional | Both | 'oauth2-redirect' (default) or 'token-exchange' |
getToken() | Required for OAuth2 mode | oauth2-redirect | Async — returns the current authentication token |
refreshToken() | Required for OAuth2 mode | oauth2-redirect | Async — refreshes the token when expired |
getExchangeToken() | Required for Token Exchange mode | token-exchange | Async — returns the external JWT, which the component exchanges for an RTC JWT |
isLoggedIn() | Required | Both | Synchronous — returns boolean indicating whether the user is authenticated |
logout() | Optional | Both | Async — called when the component needs to terminate the session |
getUserId() | Optional | Both | Synchronous — returns the current user’s unique ID. Strongly recommended; otherwise all users share the same IndexedDB |
deviceId | Required | Both | Device unique identifier, must match the Device ID embedded in the server-issued JWT |
For detailed guidance, see Authentication & Authorization.
Attributes
Section titled “Attributes”| Attribute | Type | Default | Description |
|---|---|---|---|
🎨 theme | "light" | "dark" | "system" | "system" | Theme mode. system follows the OS setting. Can be set dynamically via JS: agent.theme = 'dark' |
🌐 lang | "zh-CN" | "en-US" | "zh-CN" | UI language. Priority: HTML attribute > localStorage > browser language > default (zh-CN). Can be set dynamically via JS: agent.lang = 'en-US' |
💾 database-name | string | "rtc-agent" | IndexedDB name prefix. The final DB name is {prefix}-{userId}. For example, <rtc-agent database-name="my-app-rtc"> produces my-app-rtc-{userId} |
📛 app-label | string | "RTC Agent" | Title bar text + minimized bubble tooltip |
🖼️ bubble-icon | string | Default icon | SVG / HTML content displayed inside the minimized bubble |
📄 scenarios-url | string | — | URL of the scenario manifest, pointing to manifest.json |
🔗 server-url | string | "" | Server address. Falls back to the current page’s domain when empty |
🔁 redirect-uri | string | window.location.origin + '/auth/callback.html' | OAuth callback URL. Supports both absolute and relative paths |
👷 worker-url | string | "" | SharedWorker file URL. Used for multi-tab WebSocket sharing. Use workerURL in factory config |
JS Properties (set via JavaScript only, not HTML attributes):
| Property | Type | Default | Description |
|---|---|---|---|
⚙️ agentConfig | object | null | Declarative function registration (recommended approach) |
📦 registry | FunctionRegistry | null | Imperative function registration (created via defineRegistry) |
🪟 windowConfig | WindowConfig | null | Window behavior configuration (mode, size, interaction limits) |
🎛️ activityBarConfig | ActivityBarConfig | null | Activity Bar button visibility configuration |
🖼️ logo | { light?: string; dark?: string } | null | Custom logo replacing the default Logo on the login page, empty state, and settings page. Supports separate light/dark SVG variants |
Quick Integration
Section titled “Quick Integration”<!-- Minimal integration --><rtc-agent></rtc-agent>
<!-- Custom theme and title --><rtc-agent theme="dark" app-label="My AI Assistant"></rtc-agent>
<!-- Custom database name prefix and language --><rtc-agent database-name="my-app-rtc" lang="en-US"></rtc-agent>
<!-- With scenario docs and function registration --><rtc-agent app-label="Order Assistant" scenarios-url="https://example.com/scenarios" .agentConfig=${{ name: 'OrderApp', persona: 'You are an order management assistant', groups: [{ /* ... */ }] }}></rtc-agent>// Window configuration (JS property, set after rtc-agent-ready)const agent = document.querySelector('rtc-agent');
agent.addEventListener('rtc-agent-ready', () => { // Embedded panel: disable drag/resize/buttons, default to maximized agent.windowConfig = { embedded: true };
// Keep only chat, hide files/settings buttons agent.activityBarConfig = { disabledActivities: ['files', 'settings'], };});Events
Section titled “Events”All events support two listening patterns: DOM events (via addEventListener) and config callbacks (via the on field in RtcAgentConfig).
💡 Event naming convention: DOM event names use the
rtc-prefix with kebab-case (e.g.rtc-theme-change). Config callback names use camelCase (e.g.themeChange). The factory function internally maps callbacks to the corresponding DOM events.
Lifecycle Events
Section titled “Lifecycle Events”| DOM Event | Config Callback | Trigger | Purpose |
|---|---|---|---|
🟢 rtc-agent-ready | on.ready | Component initialization complete | Safe to access the component instance and set attributes |
🟡 rtc-before-destroy | on.beforeDestroy | disconnectedCallback runs, before cleanup | Last chance to read state or cancel teardown |
🎨 rtc-theme-change | on.themeChange | Theme changes (attribute, JS property, or system preference) | Sync external UI with the component’s theme. event.detail is { theme: 'light' | 'dark' | 'system' } |
Message Interception Events
Section titled “Message Interception Events”| DOM Event | Config Callback | Trigger | Purpose |
|---|---|---|---|
📨 rtc-before-message-send | on.beforeMessageSend | User submits a message, before it is sent | Inspect or modify the message. Return false to cancel the send |
The beforeMessageSend callback receives { message: { content: string, metadata?: Record<string, unknown> } } and must return boolean | Promise<boolean>. Return false to cancel the send. You can also mutate message.content in place to rewrite the message before it goes out.
Connection & Auth Events
Section titled “Connection & Auth Events”| DOM Event | Config Callback | Trigger | event.detail |
|---|---|---|---|
🔌 rtc-connection-retry | on.connectionRetry | User clicks the retry button | void |
🔐 rtc-auth-login-requested | on.authLoginRequested | User requests login | void |
❌ rtc-auth-refresh-failed | on.authError | Auth refresh failed | void |
🚪 rtc-auth-logout | on.authLogout | User logs out | void |
📡 rtc-connection-state-change | on.connectionStateChange | Connection state transitions | { state: ConnectionState } |
✅ rtc-auth-login | on.authLogin | Login successful (incl. initial load, refresh, manual login) | { userId: string } |
Session & Message Events
Section titled “Session & Message Events”| DOM Event | Config Callback | Trigger | event.detail |
|---|---|---|---|
📝 rtc-session-created | on.sessionCreated | New session created | { session: Session } |
🔀 rtc-session-switched | on.sessionSwitched | Session switched | { id: string } |
✏️ rtc-session-renamed | on.sessionRenamed | Session renamed | { id: string, title: string } |
🗑️ rtc-session-deleted | on.sessionDeleted | Session deleted | { id: string } |
📥 rtc-message-received | on.messageReceived | AI message received | { message: Message } |
📤 rtc-message-sent | on.messageSent | User message sent | { message: Message } |
Tool Call Events (Config Callbacks Only)
Section titled “Tool Call Events (Config Callbacks Only)”Tool call events are bridged from the internal EventBus and only support the on.* config callback pattern — they are not dispatched as DOM events. This avoids putting high-frequency tool call events on the DOM event system.
| Config Callback | Trigger | Callback Args | EventBus Source |
|---|---|---|---|
on.toolCallStart | Tool call begins | { path, params } | function:start |
on.toolCallSuccess | Tool call completes successfully | { path, result } | function:success |
on.toolCallError | Tool call fails | { path, error } | function:error |
on.toolCallProgress | Tool call reports intermediate progress | { path, progress } | function:progress |
path is the full function path (e.g. "myGroup.myFunction"); params are the validated input arguments.
Event Examples
Section titled “Event Examples”const agent = createRtcAgent({ // ...other config on: { // Lifecycle ready: () => { console.log('RTC Agent is ready'); }, beforeDestroy: () => { console.log('Component is about to be destroyed'); }, themeChange: (detail) => { console.log('Theme changed to:', detail.theme); document.body.dataset.theme = detail.theme; },
// Message interception — validate before send beforeMessageSend: async ({ message }) => { // Block empty messages if (!message.content.trim()) { return false; } // Append a signature message.content += '\n\n— Sent from My App'; return true; },
// Tool call tracking (config callbacks only, not DOM events) toolCallStart: ({ path, params }) => { console.log(`Tool call started: ${path}`, params); }, toolCallSuccess: ({ path, result }) => { console.log(`Tool call succeeded: ${path}`, result); }, toolCallError: ({ path, error }) => { console.error(`Tool call failed: ${path}`, error); }, toolCallProgress: ({ path, progress }) => { updateProgressBar(path, progress); }, },});The same lifecycle events can also be listened to via DOM addEventListener (useful when you cannot set config callbacks):
const agent = document.querySelector('rtc-agent');
agent.addEventListener('rtc-agent-ready', () => { // Component is ready, safe to operate agent.theme = 'dark'; agent.lang = 'en-US'; agent.appLabel = 'Custom Title';});
agent.addEventListener('rtc-theme-change', (e) => { console.log('Theme is now:', e.detail.theme);});
agent.addEventListener('rtc-before-message-send', (e) => { const { message } = e.detail; if (containsSensitiveWords(message.content)) { e.preventDefault(); // cancel send }});💡 Best Practice: Always wait for the
rtc-agent-readyevent before interacting with the component to avoid errors caused by uninitialized state.
Window System
Section titled “Window System”<rtc-agent> includes three built-in window modes, supporting floating, fullscreen, and minimized bubble:
| Interaction | Description |
|---|---|
| 🖱️ Drag | The title bar serves as the drag handle |
| ↔️ Resize | Supports 8-directional resize operations |
| ⌨️ Keyboard | Arrow keys move the window; Shift to accelerate |
| 📏 Viewport constraint | Window always stays within the visible area and cannot be dragged off-screen |
Window Configuration
Section titled “Window Configuration”Use the windowConfig property to control default window behavior and interaction limits:
agent.windowConfig = { // Default window mode defaultMode: 'maximized', // 'normal' | 'maximized' | 'minimized'
// Embedded mode (shortcut) // Equivalent to: defaultMode: 'maximized' + draggable: false + resizable: false // + showMinimize: false + showMaximize: false // CSS uses position: relative to fill the parent container (instead of position: fixed filling the viewport) embedded: true,
// Fine-grained control draggable: false, // Whether the window can be dragged resizable: false, // Whether the window can be resized showMinimize: false, // Whether to show the minimize button showMaximize: false, // Whether to show the maximize button showClose: false, // Whether to show the close button
// Size and position initialSize: { width: 420, height: 640 }, initialPosition: { x: 100, y: 100 }, minWidth: 350, minHeight: 520, maxWidth: Infinity, maxHeight: Infinity,
// Minimized bubble position bubblePosition: { corner: 'bottom-right', // Origin corner of the coordinate system offset: { x: -20, y: 20 }, // Cartesian coordinate offset },};Bubble Position Configuration
Section titled “Bubble Position Configuration”bubblePosition uses a mathematical Cartesian coordinate system to control the position of the minimized bubble:
| Field | Type | Description |
|---|---|---|
corner | 'top-left' | 'top-right' | 'bottom-left' | 'bottom-right' | The corner of the host application where the coordinate origin is placed |
offset.x | number | Horizontal offset (positive = right, negative = left) |
offset.y | number | Vertical offset (positive = up, negative = down, mathematical coordinate system) |
Examples:
// Bottom-right corner, 20px inward offset (default)bubblePosition: { corner: 'bottom-right', offset: { x: -20, y: 20 } }
// Top-left corner, 20px offset to bottom-rightbubblePosition: { corner: 'top-left', offset: { x: 20, y: -20 } }
// Bottom-left corner, 30px offset to top-rightbubblePosition: { corner: 'bottom-left', offset: { x: 30, y: 30 } }💡 Expand Direction: On the first restore from minimized state, the window expands based on the
cornerconfiguration. For example, withcorner: 'bottom-right', the window’s bottom-right corner aligns with the bubble position, expanding toward the upper-left. Subsequent minimize/restore cycles use the remembered position.
Common Scenarios:
| Scenario | Configuration |
|---|---|
| Embedded panel | { embedded: true } |
| Fixed position window | { draggable: false, resizable: false } |
| No minimize button | { showMinimize: false, defaultMode: 'maximized' } |
| Floating chat window | null (uses defaults) |
Activity Bar Configuration
Section titled “Activity Bar Configuration”Use the activityBarConfig property to control button visibility in the Activity Bar:
agent.activityBarConfig = { // Activities to hide (chat is always visible and cannot be hidden) disabledActivities: ['files', 'settings'],
// Default active activity defaultActivity: 'chat',};| Activity | Description | Hideable |
|---|---|---|
💬 chat | Chat interface | ❌ Always visible |
📁 files | File manager | ✅ via disabledActivities |
🛠️ functions | Function Debugger panel | ✅ via disabledActivities |
⚙️ settings | Settings panel | ✅ via disabledActivities |
Function Debugger
Section titled “Function Debugger”The Function Debugger is an optional panel in the Activity Bar that provides a complete function debugging interface:
- Function Tree: Displays all registered functions grouped by their function groups, with expand/collapse state persisted to localStorage
- Parameter Editor: JSON syntax highlighting + validation, with Zod schema auto-derivation for parameter templates
- Execution Console: Color-coded log output (info/warn/error/debug) with real-time execution results
- Execution History: Bottom drawer panel with pagination, per-function filtering, and full details (params, logs, result) for each entry
- Parameter Docs: Recursively expanded parameter table reusing
schemaToTypeStringfor type strings
Add 'functions' to disabledActivities to hide the Function Debugger button (suitable for end-user scenarios where debugging is not needed).
State Management
Section titled “State Management”The component uses 23 Controllers internally to manage state. 11 core state Controllers handle business logic, and 12 UI Controllers handle interface interactions. Controllers do not reference each other directly; instead, the root component <rtc-agent> acts as the central hub orchestrating cross-Controller communication:
💡 Design Principle: Controllers are decoupled from each other; all cross-Controller communication goes through the root component. Sub-components obtain state via
@lit/contextand do not hold direct Controller references.
Internationalization (i18n)
Section titled “Internationalization (i18n)”RTC Agent includes complete internationalization support, implemented with @lit/localize for runtime language switching.
Supported Languages
Section titled “Supported Languages”| Language Code | Language | Description |
|---|---|---|
zh-CN | Simplified Chinese | Default language (source) |
en-US | English | Target language |
Switching Languages
Section titled “Switching Languages”Switch the interface language using the switchLocale() API:
import { switchLocale } from '@rtc-agent/component';
// Switch to Englishawait switchLocale('en-US');
// Switch to Chineseawait switchLocale('zh-CN');Language Persistence
Section titled “Language Persistence”Language selection is automatically saved to localStorage (key: rtc-agent-locale) and persists across page refreshes.
Using in Components
Section titled “Using in Components”Use the @localized() decorator and msg() function in components:
import { localized, msg } from '@lit/localize';import { consume } from '@lit/context';import { localeContext } from '@rtc-agent/component';
@localized()@customElement('my-component')export class MyComponent extends LitElement { @consume({ context: localeContext, subscribe: true }) private _localeCtx!: LocaleContextValue;
render() { // React to locale changes void this._localeCtx.locale;
return html` <div>${msg('Welcome to RTC Agent')}</div> `; }}Adding Translations
Section titled “Adding Translations”- Mark text: Wrap translatable text with
msg()in components - Extract translations: Run
npm run localize:extractto generate XLIFF files - Translate text: Edit
xliff/en-US.xlffile - Build translations: Run
npm run localize:buildto generate language packs
For detailed guidance, see Internationalization Integration Guide.
RTC Agent supports light, dark, and system-follow themes, switchable at runtime via the switchTheme() API.
Switching Theme
Section titled “Switching Theme”import { switchTheme } from '@rtc-agent/component';
// Switch to dark themeswitchTheme('dark');
// Switch to light themeswitchTheme('light');
// Follow system preferenceswitchTheme('system');switchTheme() simultaneously:
- Updates the
themeproperty on all<rtc-agent>elements - Persists to
localStorage(key:rtc-agent-theme) - Sets the
data-themeattribute ondocument.documentElement(resolved effective theme)
Querying Theme
Section titled “Querying Theme”import { getStoredTheme, getEffectiveTheme } from '@rtc-agent/component';
// User preference (may be 'system')const preference = getStoredTheme(); // 'light' | 'dark' | 'system'
// Resolved effective theme (always 'light' or 'dark')const effective = getEffectiveTheme(); // 'light' | 'dark'Initializing Theme
Section titled “Initializing Theme”Call initTheme() at app startup to restore the theme preference from localStorage:
import { initTheme } from '@rtc-agent/component';
initTheme();Theme Events
Section titled “Theme Events”Listen for the rtc-theme-change event to sync external UI:
agent.addEventListener('rtc-theme-change', (e) => { console.log('Theme switched to:', e.detail.theme);});For detailed guidance, see Settings System.
CSS Variables
Section titled “CSS Variables”CSS variables allow you to customize the component’s appearance and dimensions without modifying source code:
rtc-agent { /* Window default dimensions */ --rtc-window-default-width: 420px; --rtc-window-default-height: 640px;
/* Minimized bubble size */ --rtc-bubble-size: 40px;
/* User-defined font size (affects all text) */ --rtc-font-size-user: 16px;
/* Brand colors */ --rtc-color-primary-rgb: 39 65 254; /* Light: #2741FE */ --rtc-color-accent: #2741FE;}| Variable | Default | Description |
|---|---|---|
--rtc-window-default-width | 420px | Default width of the floating window |
--rtc-window-default-height | 640px | Default height of the floating window |
--rtc-bubble-size | 40px | Diameter of the minimized bubble |
--rtc-font-size-user | 14px | User-defined base font size (12-24px) |
--rtc-color-primary-rgb | Light: 39 65 254Dark: 26 122 176 | Primary color RGB value (for opacity calculations) |
--rtc-color-accent | Light: #2741FEDark: #1A7AB0 | Accent color (links, buttons, etc.) |
Font Size Scale
Section titled “Font Size Scale”Setting --rtc-font-size-user proportionally scales all font sizes:
| Token | Formula | Example (base=14px) |
|---|---|---|
--rtc-font-size-xs | base * 0.857 | 12px |
--rtc-font-size-sm | base * 0.929 | 13px |
--rtc-font-size-base | base | 14px |
--rtc-font-size-md | base * 1.143 | 16px |
--rtc-font-size-lg | base * 1.286 | 18px |
--rtc-font-size-xl | base * 1.429 | 20px |
--rtc-font-size-2xl | base * 1.714 | 24px |
Logo Customization
Section titled “Logo Customization”Use the logo property to replace the default brand Logo on the login page, empty state, and settings page. It supports separate light and dark SVG variants that automatically switch with the theme.
const agent = createRtcAgent({ // ...other config logo: { light: '<svg viewBox="0 0 80 80" xmlns="http://www.w3.org/2000/svg"><circle cx="40" cy="40" r="36" fill="#2741FE"/><text x="40" y="52" text-anchor="middle" fill="white" font-size="32" font-weight="bold">M</text></svg>', dark: '<svg viewBox="0 0 80 80" xmlns="http://www.w3.org/2000/svg"><circle cx="40" cy="40" r="36" fill="#1A7AB0"/><text x="40" y="52" text-anchor="middle" fill="white" font-size="32" font-weight="bold">M</text></svg>', },});You can also set only one variant — missing variants fall back to the default Logo:
agent.logo = { light: '<svg>...</svg>', // only replace the Logo in light theme // dark is not set — dark theme uses the default Logo};Internal Mechanism
Section titled “Internal Mechanism”The component internally uses the <rtc-logo> sub-component + Lit Context API to implement automatic theme switching:
<rtc-logo>consumesLogoContextand renders the corresponding Logo variant based on the current theme (light/dark)- Supports the
themeattribute:'light'|'dark'|'system', wheresystemlistens to theprefers-color-schememedia query - If no custom Logo is provided, it renders the default RTC Agent Logo
Bubble Icon (bubbleIcon)
Section titled “Bubble Icon (bubbleIcon)”bubbleIcon and logo are two independent concepts:
| Property | Location | Purpose |
|---|---|---|
bubbleIcon | Minimized bubble | Icon inside the circular bubble after the window is minimized |
logo | Login page, empty state, settings page | Brand identity for displaying company/product branding |
Both can be customized with SVG, but they serve different purposes.
Default Logo Files
Section titled “Default Logo Files”To modify the default Logo, replace the following SVG files:
| File | Description | Usage |
|---|---|---|
logo.svg | Blue sky Logo | Light theme |
logo-dark.svg | Night sky Logo | Dark theme |
Connection & Retry
Section titled “Connection & Retry”RTC Agent automatically attempts to establish a connection when the component initializes. Connection state is managed through an internal state machine, with the frontend reflecting connection progress in real-time.
Connection Flow
Section titled “Connection Flow”Design Principle: Connection failures do not trigger automatic retries. Instead, control is handed to the user, who can check network conditions before manually triggering a reconnection. This avoids wasting resources on repeated attempts during network outages.
Manual Reconnection
Section titled “Manual Reconnection”When connection fails, users can manually reconnect in the following ways:
Method 1: Click the title bar retry button
Section titled “Method 1: Click the title bar retry button”After connection fails, a red retry button appears in the title bar. Click to trigger reconnection.
Method 2: Call the reconnect() method
Section titled “Method 2: Call the reconnect() method”const agent = document.querySelector('rtc-agent');
// Manually trigger reconnectionawait agent.reconnect();Connection State Query
Section titled “Connection State Query”Connection state can be queried through the following read-only properties:
| Property | Type | Description |
|---|---|---|
connectionFailed | boolean | Whether connection has failed |
connectionError | string | Error message when connection failed |
// Check connection stateagent.addEventListener('rtc-agent-ready', () => { console.log('Connection failed:', agent.connectionFailed); console.log('Connection error:', agent.connectionError);
if (agent.connectionFailed) { console.error('Connection failed:', agent.connectionError); }});Note: Detailed connection states (
disconnected,connecting,connected,reconnecting) are managed internally by the component and not exposed externally. External code can only determine connection success viaconnectionFailedandconnectionError.
Title Bar Status Display
Section titled “Title Bar Status Display”The title bar shows different visual feedback based on connection state:
| State | Status Dot Color | Text | Retry Button |
|---|---|---|---|
connected | 🟢 Green | “Connected” | ❌ Hidden |
connecting | 🟡 Yellow pulse | “Connecting” | ❌ Hidden |
reconnecting | 🟡 Yellow pulse | “Reconnecting” | ❌ Hidden |
disconnected | 🔴 Red | “Disconnected” | ❌ Hidden |
| Connection failed | 🔴 Red blinking | “Connection failed: …” | ✅ Shown |
Style System
Section titled “Style System”The component uses a three-layer style architecture, progressing from foundational tokens to top-level variables:
| Layer | Content | Description |
|---|---|---|
| 🏗️ Design Tokens | Spacing, typography, radius, shadow, transition, z-index | Foundational design constants ensuring visual consistency |
| 🌓 Color Themes | Light / Dark (VS Code style) | Two complete color schemes with automatic adaptation |
| 🔧 CSS Variables | Component-level customization (window dimensions, bubble size) | Overridable by host applications for personalization |
Debug API
Section titled “Debug API”<rtc-agent> exposes a window.rtcAgentDebug object in all builds (dev + prod), providing 40+ methods for E2E testing, debugging, and automation.
const debug = window.rtcAgentDebug;Method Categories
Section titled “Method Categories”| Domain | Methods | Description |
|---|---|---|
| State | getState(), clearData(), seedData() | State access and data management |
| Auth | loginAs(userId, tokens?), logout() | Authentication simulation |
| VirtualFS | listFiles(path?), readFile(path), writeFile(path, content), deleteFile(path) | Virtual file system operations |
| Session | createSession(), switchSession(id), deleteSession(id), renameSession(id, title), getSessions(), getCurrentSessionId() | Session management |
| Message | sendMessage(content), getMessages(), addDemoMessage(content, role?), clearMessages() | Message operations |
| Tool Call | getToolCalls(), addPendingToolCall(call), approveToolCall(id), denyToolCall(id), approveAllToolCalls(toolName) | Tool call simulation |
| UI Control | click(selector), scrollIntoView(selector), typeText(selector, text) | UI interaction simulation |
| Toast | showToast(message, type?), getToasts() | Toast notifications |
| Settings | getSettings(), updateSettings(section, patch) | Settings management |
| Activity | setActivity(activity), getActivity() | Activity Bar control |
| Network | simulateOffline(), restoreNetwork(), isOffline | Network simulation |
| Event | triggerEvent(name, detail?) | Custom event simulation |
| Metrics | getMetrics() | Performance metrics collection |
| Component | element, waitForReady(timeout?), waitForConnected(timeout?) | Component reference and waiting |
| Logs | logs, clearLogs() | Log access |
Usage Example
Section titled “Usage Example”const debug = window.rtcAgentDebug;
// Wait for component to be readyconst agent = await debug.waitForReady(5000);
// Wait for connectionawait debug.waitForConnected(10000);
// Create a session and send a messageconst sessionId = debug.createSession();debug.switchSession(sessionId);await debug.sendMessage('Hello, help me write a Hello World');
// Simulate offline modedebug.simulateOffline();console.log('Is offline:', debug.isOffline);
// Restore networkdebug.restoreNetwork();
// Get all messagesconst messages = debug.getMessages();console.log('Message count:', messages.length);
// Get current stateconst state = debug.getState();console.log('Current session:', debug.getCurrentSessionId());E2E Testing Best Practices
Section titled “E2E Testing Best Practices”// Playwright exampletest('send message and get response', async ({ page }) => { await page.goto('/');
// Wait for component to be ready await page.waitForFunction(() => window.rtcAgentDebug?.waitForReady); await page.evaluate(() => window.rtcAgentDebug.waitForReady(5000));
// Send a message await page.evaluate(() => window.rtcAgentDebug.sendMessage('Hello'));
// Wait for AI response await page.waitForFunction(() => { const msgs = window.rtcAgentDebug.getMessages(); return msgs.some(m => m.role === 'assistant'); }, { timeout: 30000 });
// Verify response const messages = await page.evaluate(() => window.rtcAgentDebug.getMessages()); expect(messages.length).toBeGreaterThan(1);});💡 The Debug API is available in production builds too, which is useful for diagnosing live issues. However, we recommend using data-modifying methods like
seedData()andclearData()only in development and test environments.
Key Interactions
Section titled “Key Interactions”| Area | Behavior |
|---|---|
| ⌨️ Input Area | Textarea + bottom toolbar; Enter to submit, Shift+Enter for newline; toolbar includes attach, commands, scenario selection, mode toggle, send/stop; supports file upload (click attach button or Ctrl/Cmd+V to paste), files are first cached to local IndexedDB then uploaded to S3 in background; message can only be sent after all files are uploaded; when scenarios-url is configured, a scenario selection button is shown in the toolbar, opening the rtc-scenario-panel component for users to pick a scenario |
| 📎 File Upload | Click the attach button to open the file picker (supports image/* and text/*), or use Ctrl/Cmd+V to paste clipboard files; files are first cached locally (FileStorage.cacheFileForUpload()), thumbnails display immediately; background upload to S3 with real-time progress display; failed uploads can be retried by clicking the retry button; send condition: all files must be in loaded state |
| 🖼️ File Preview | Click a thumbnail to open a fullscreen preview modal (rtc-file-preview-modal); supports image preview (object-fit: contain) and text preview (monospace font, large files truncated to 1000 lines); three close methods: ESC key, backdrop click, close button; falls back to S3 presigned URL when download fails |
| 📨 Message List | Auto-scrolls to bottom; “New messages” button shown when user scrolls away; supports Markdown rendering and code highlighting |
| ⚡ Tool Confirmation Dialog | Displays tool name and parameters; Yes / No buttons; clicking the background is equivalent to rejecting |
| 🔄 Connection Retry | A retry button is displayed when the connection fails; you can also call agent.reconnect() programmatically to reconnect |
| 🗂️ Tool Call Cards | Each tool call is displayed as a timeline card. Clicking the timeline dot copies the current content to the clipboard (input cards copy parameters, reply cards copy output). For the todoWrite tool, cards render as a task list with status icons (✓ completed / ● in progress / ○ pending) instead of raw JSON |
File Upload and Preview
Section titled “File Upload and Preview”File Upload Flow
Section titled “File Upload Flow”Upload States
Section titled “Upload States”Each file has three states during upload, managed via the uploadStates Map:
| State | Icon | Description |
|---|---|---|
loading | 🔄 Progress ring | Uploading to S3, showing 0-100% progress |
loaded | ✅ Complete | Upload successful, thumbnail displays normally |
error | ❌ Error | Upload failed, retry button shown |
Send Conditions
Section titled “Send Conditions”Send button availability is controlled by the _canSubmit computed property:
// Simplified logicget _canSubmit(): boolean { if (!this._hasContent) return false; // Check if any files are uploading or failed const hasUploadingOrFailedFiles = Array.from(this._uploadStates.values()) .some(state => state === 'loading' || state === 'error'); return !hasUploadingOrFailedFiles;}_hasContentis true when there is text or file attachments- Send button is enabled when all files are in
loadedstate - Send button is disabled when any file is in
loadingorerrorstate
File Preview Components
Section titled “File Preview Components”File preview related sub-components:
| Component | Purpose | Key Features |
|---|---|---|
rtc-file-preview-area | Attachment preview area container | Horizontal scrolling list showing all attachment thumbnails; supports edit mode (shows delete buttons) and readonly mode (message display) |
rtc-file-thumbnail | Single file thumbnail | Shows thumbnail for image files (object-fit: cover), file icon for text files; displays upload progress ring, sync status badge, filename |
rtc-file-preview-modal | Fullscreen preview modal | Image preview (object-fit: contain), text preview (monospace font, large file truncation); three close methods: ESC, backdrop click, close button |
FileStorage Context:
File preview components obtain the FileStorage instance via FileStorageContext for:
- Loading thumbnails:
fileStorage.getThumbnailUrl({md5, ext}) - Downloading files:
fileStorage.download(md5, ext, {signal}) - Getting presigned URLs:
fileStorage.getPresignedUrl({md5, ext}, expires)(fallback approach)
FileStorage Public API
Section titled “FileStorage Public API”FileStorage is the high-level file storage abstraction exposed by the component, obtained via FileStorageContext. It supports an offline-first upload/download lifecycle, batch operations, and cache management.
Core Methods
Section titled “Core Methods”| Method | Signature | Description |
|---|---|---|
cacheFileForUpload | (file: File) => Promise<FileInfo> | Offline-first: immediately writes the file to local IndexedDB cache (calculates MD5), returns FileInfo (syncStatus: 'pending'). Thumbnails display immediately; actual S3 upload is performed by a subsequent upload() call |
upload | (opts: UploadOptions) => Promise<FileInfo> | Uploads the file to S3. Supports onProgress callback and signal for abort. After upload, syncStatus changes to 'synced' |
download | (md5: string, ext: string, opts?: DownloadOptions) => Promise<Blob> | Downloads a file. Reads from local cache first; falls back to S3 when cache misses. Supports forceRefresh to bypass cache |
getThumbnailUrl | (opts: {md5, ext}) => Promise<string> | Gets a thumbnail Blob URL. Uses shared cache with reference counting to avoid duplicates |
releaseThumbnailUrl | (url: string) => void | Releases a thumbnail Blob URL reference. Revokes the blob when refcount reaches zero |
getPresignedUrl | (opts: {md5, ext}, expires?: number) => Promise<string> | Gets an S3 presigned URL (fallback approach) |
Batch Operations
Section titled “Batch Operations”| Method | Signature | Description |
|---|---|---|
uploadBatch | (files: File[], opts?: BatchOptions) => Promise<FileInfo[]> | Batch upload files with concurrency control and error handling strategy ('continue' skips failures / 'cancel' aborts all) |
downloadBatch | (items: {md5, ext}[], opts?: BatchOptions) => Promise<Blob[]> | Batch download files |
deleteBatch | (items: {md5, ext}[]) => Promise<void> | Batch delete files (local cache + S3) |
Cache Management
Section titled “Cache Management”| Method | Signature | Description |
|---|---|---|
getCacheStats | () => Promise<CacheStats> | Returns cache statistics (total files, total size, etc.) |
evictCache | (opts?: {maxAge?: number}) => Promise<number> | Evicts expired cache files, returns the number evicted |
resumeInterruptedUploads | () => Promise<void> | Resumes interrupted uploads (call after network recovery) |
💡 Offline-First:
cacheFileForUpload()writes the file to IndexedDB immediately, so thumbnails display even without network. A subsequentupload()syncs the file to S3 —syncStatustransitions from'pending'to'syncing'to'synced'(or'failed').UploadOptions.cacheTtlMscontrols local cache expiration (default: 7 days).
Next Steps
Section titled “Next Steps”- Authentication & Authorization — Learn about the login flow and token mechanism
- Function Registration Guide — Register custom functions via
agentConfig - Scenario Authoring Guide — Write scenario documents to guide AI behavior