UNPKG

@microsoft/agents-hosting

Version:

Microsoft 365 Agents SDK for JavaScript

540 lines (539 loc) 23.6 kB
/** * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. */ import { Activity, ConversationReference } from '@microsoft/agents-activity'; import { BaseAdapter } from '../baseAdapter'; import { ResourceResponse } from '../connector-client'; import { TurnContext } from '../turnContext'; import { AdaptiveCardsActions } from './adaptiveCards'; import { AgentApplicationOptions } from './agentApplicationOptions'; import { ConversationUpdateEvents } from './conversationUpdateEvents'; import { AgentExtension } from './extensions'; import { Authorization } from './authorization'; import { RouteHandler } from './routeHandler'; import { RouteSelector } from './routeSelector'; import { TurnEvents } from './turnEvents'; import { TurnState } from './turnState'; import { RouteRank } from './routeRank'; import { RouteList } from './routeList'; /** * Event handler function type for application events. * @typeParam TState - The state type extending TurnState. * @param context - The turn context containing activity information. * @param state - The current turn state. * @returns A promise that resolves to a boolean indicating whether to continue execution. */ export type ApplicationEventHandler<TState extends TurnState> = (context: TurnContext, state: TState) => Promise<boolean>; /** * @summary Main application class for handling agent conversations and routing. * * @remarks * The AgentApplication class provides a framework for building conversational agents. * It handles routing activities to appropriate handlers, manages conversation state, * supports authentication flows, and provides various event handling capabilities. * * Key features: * - Activity routing based on type, content, or custom selectors * - State management with automatic load/save * - OAuth authentication support * - Typing indicators and long-running message support * - Extensible architecture with custom extensions * - Event handlers for before/after turn processing * * @example * ```typescript * const app = new AgentApplication<MyState>({ * storage: new MemoryStorage(), * adapter: myAdapter * }); * * app.onMessage('hello', async (context, state) => { * await context.sendActivity('Hello there!'); * }); * * await app.run(turnContext); * ``` * * @typeParam TState - The state type extending TurnState. */ export declare class AgentApplication<TState extends TurnState> { protected readonly _options: AgentApplicationOptions<TState>; protected readonly _routes: RouteList<TState>; protected readonly _beforeTurn: ApplicationEventHandler<TState>[]; protected readonly _afterTurn: ApplicationEventHandler<TState>[]; private readonly _adapter?; private readonly _authorization?; private _typingTimer; protected readonly _extensions: AgentExtension<TState>[]; private readonly _adaptiveCards; /** * @summary Creates a new instance of AgentApplication. * * @param options - Optional configuration options for the application. * * @remarks * The constructor initializes the application with default settings and applies * any provided options. It sets up the adapter, authorization, and other core * components based on the configuration. * * Default options: * - startTypingTimer: false * - longRunningMessages: false * - removeRecipientMention: true * - turnStateFactory: Creates a new TurnState instance * * @example * ```typescript * const app = new AgentApplication({ * storage: new MemoryStorage(), * adapter: myAdapter, * startTypingTimer: true, * authorization: { connectionName: 'oauth' }, * transcriptLogger: myTranscriptLogger, * }); * ``` */ constructor(options?: Partial<AgentApplicationOptions<TState>>); /** * @summary Gets the authorization instance for the application. * * @returns The authorization instance. * @throws Error if no authentication options were configured. */ get authorization(): Authorization; /** * @summary Gets the options used to configure the application. * * @returns The application options. */ get options(): AgentApplicationOptions<TState>; /** * @summary Gets the adapter used by the application. * * @returns The adapter instance. */ get adapter(): BaseAdapter; /** * @summary Gets the adaptive cards actions handler for the application. * * @returns The adaptive cards actions instance. * * @remarks * The adaptive cards actions handler provides functionality for handling * adaptive card interactions, such as submit actions and other card-based events. * * @example * ```typescript * app.adaptiveCards.actionSubmit('doStuff', async (context, state, data) => { * await context.sendActivity(`Received data: ${JSON.stringify(data)}`); * }); * ``` */ get adaptiveCards(): AdaptiveCardsActions<TState>; /** * Sets an error handler for the application. * * @param handler - The error handler function to be called when an error occurs. * @returns The current instance of the application. * * @remarks * This method allows you to handle any errors that occur during turn processing. * The handler will receive the turn context and the error that occurred. * * @example * ```typescript * app.onError(async (context, error) => { * console.error(`An error occurred: ${error.message}`); * await context.sendActivity('Sorry, something went wrong!'); * }); * ``` */ onError(handler: (context: TurnContext, error: Error) => Promise<void>): this; /** * Adds a new route to the application for handling activities. * * @param selector - The selector function that determines if a route should handle the current activity. * @param handler - The handler function that will be called if the selector returns true. * @param isInvokeRoute - Whether this route is for invoke activities. Defaults to false. * @param rank - The rank of the route, used to determine the order of evaluation. Defaults to RouteRank.Unspecified. * @param authHandlers - Array of authentication handler names for this route. Defaults to empty array. * @returns The current instance of the application. * * @remarks * Routes are evaluated by rank order (if provided), otherwise, in the order they are added. * Invoke-based activities receive special treatment and are matched separately as they typically * have shorter execution timeouts. * * @example * ```typescript * app.addRoute( * async (context) => context.activity.type === ActivityTypes.Message, * async (context, state) => { * await context.sendActivity('I received your message'); * }, * false, // isInvokeRoute * RouteRank.First // rank * ); * ``` */ addRoute(selector: RouteSelector, handler: RouteHandler<TState>, isInvokeRoute?: boolean, rank?: number, authHandlers?: string[]): this; /** * Adds a handler for specific activity types. * * @param type - The activity type(s) to handle. Can be a string, RegExp, RouteSelector, or array of these types. * @param handler - The handler function that will be called when the specified activity type is received. * @param authHandlers - Array of authentication handler names for this activity. Defaults to empty array. * @param rank - The rank of the route, used to determine the order of evaluation. Defaults to RouteRank.Unspecified. * @returns The current instance of the application. * * @remarks * This method allows you to register handlers for specific activity types such as 'message', 'conversationUpdate', etc. * You can specify multiple activity types by passing an array. * * @example * ```typescript * app.onActivity(ActivityTypes.Message, async (context, state) => { * await context.sendActivity('I received your message'); * }); * ``` */ onActivity(type: string | RegExp | RouteSelector | (string | RegExp | RouteSelector)[], handler: (context: TurnContext, state: TState) => Promise<void>, authHandlers?: string[], rank?: RouteRank): this; /** * Adds a handler for conversation update events. * * @param event - The conversation update event to handle (e.g., 'membersAdded', 'membersRemoved'). * @param handler - The handler function that will be called when the specified event occurs. * @param authHandlers - Array of authentication handler names for this event. Defaults to empty array. * @param rank - The rank of the route, used to determine the order of evaluation. Defaults to RouteRank.Unspecified. * @returns The current instance of the application. * @throws Error if the handler is not a function. * * @remarks * Conversation update events occur when the state of a conversation changes, such as when members join or leave. * * @example * ```typescript * app.onConversationUpdate('membersAdded', async (context, state) => { * const membersAdded = context.activity.membersAdded; * for (const member of membersAdded) { * if (member.id !== context.activity.recipient.id) { * await context.sendActivity('Hello and welcome!'); * } * } * }); * ``` */ onConversationUpdate(event: ConversationUpdateEvents, handler: (context: TurnContext, state: TState) => Promise<void>, authHandlers?: string[], rank?: RouteRank): this; /** * Continues a conversation asynchronously. * * @param conversationReferenceOrContext - The conversation reference or turn context. * @param logic - The logic to execute during the conversation. * @returns A promise that resolves when the conversation logic has completed. * @throws Error if the adapter is not configured. */ protected continueConversationAsync(conversationReferenceOrContext: ConversationReference | TurnContext, logic: (context: TurnContext) => Promise<void>): Promise<void>; /** * Adds a handler for message activities that match the specified keyword or pattern. * * @param keyword - The keyword, pattern, or selector function to match against message text. * Can be a string, RegExp, RouteSelector, or array of these types. * @param handler - The handler function that will be called when a matching message is received. * @param authHandlers - Array of authentication handler names for this message handler. Defaults to empty array. * @param rank - The rank of the route, used to determine the order of evaluation. Defaults to RouteRank.Unspecified. * @returns The current instance of the application. * * @remarks * This method allows you to register handlers for specific message patterns. * If keyword is a string, it matches messages containing that string. * If keyword is a RegExp, it tests the message text against the regular expression. * If keyword is a function, it calls the function with the context to determine if the message matches. * * @example * ```typescript * app.onMessage('hello', async (context, state) => { * await context.sendActivity('Hello there!'); * }); * * app.onMessage(/help/i, async (context, state) => { * await context.sendActivity('How can I help you?'); * }); * ``` */ onMessage(keyword: string | RegExp | RouteSelector | (string | RegExp | RouteSelector)[], handler: (context: TurnContext, state: TState) => Promise<void>, authHandlers?: string[], rank?: RouteRank): this; /** * Sets a handler to be called when a user successfully signs in. * * @param handler - The handler function to be called after successful sign-in. * @returns The current instance of the application. * @throws Error if authentication options were not configured. * * @remarks * This method allows you to perform actions after a user has successfully authenticated. * The handler will receive the turn context and state. * * @example * ```typescript * app.onSignInSuccess(async (context, state) => { * await context.sendActivity('You have successfully signed in!'); * }); * ``` */ onSignInSuccess(handler: (context: TurnContext, state: TurnState, id?: string) => Promise<void>): this; /** * Sets a handler to be called when a sign-in attempt fails. * * @param handler - The handler function to be called after a failed sign-in attempt. * @returns The current instance of the application. * @throws Error if authentication options were not configured. * * @remarks * This method allows you to handle cases where a user fails to authenticate, * such as when they cancel the sign-in process or an error occurs. * * @example * ```typescript * app.onSignInFailure(async (context, state) => { * await context.sendActivity('Sign-in failed. Please try again.'); * }); * ``` */ onSignInFailure(handler: (context: TurnContext, state: TurnState, id?: string) => Promise<void>): this; /** * Adds a handler for message reaction added events. * * @param handler - The handler function that will be called when a message reaction is added. * @param rank - The rank of the route, used to determine the order of evaluation. Defaults to RouteRank.Unspecified. * @returns The current instance of the application. * * @remarks * This method registers a handler that will be invoked when a user adds a reaction to a message, * such as a like, heart, or other emoji reaction. * * @example * ```typescript * app.onMessageReactionAdded(async (context, state) => { * const reactionsAdded = context.activity.reactionsAdded; * if (reactionsAdded && reactionsAdded.length > 0) { * await context.sendActivity(`Thanks for your ${reactionsAdded[0].type} reaction!`); * } * }); * ``` */ onMessageReactionAdded(handler: (context: TurnContext, state: TState) => Promise<void>, rank?: RouteRank): this; /** * Adds a handler for message reaction removed events. * * @param handler - The handler function that will be called when a message reaction is removed. * @param rank - The rank of the route, used to determine the order of evaluation. Defaults to RouteRank.Unspecified. * @returns The current instance of the application. * * @remarks * This method registers a handler that will be invoked when a user removes a reaction from a message, * such as unliking or removing an emoji reaction. * * @example * ```typescript * app.onMessageReactionRemoved(async (context, state) => { * const reactionsRemoved = context.activity.reactionsRemoved; * if (reactionsRemoved && reactionsRemoved.length > 0) { * await context.sendActivity(`You removed your ${reactionsRemoved[0].type} reaction.`); * } * }); * ``` */ onMessageReactionRemoved(handler: (context: TurnContext, state: TState) => Promise<void>, rank?: RouteRank): this; /** * Executes the application logic for a given turn context. * * @param turnContext - The context for the current turn of the conversation. * @returns A promise that resolves when the application logic has completed. * * @remarks * This method is the entry point for processing a turn in the conversation. * It delegates the actual processing to the `runInternal` method, which handles * the core logic for routing and executing handlers. * * @example * ```typescript * const app = new AgentApplication(); * await app.run(turnContext); * ``` */ run(turnContext: TurnContext): Promise<void>; /** * Executes the application logic for a given turn context. * * @param turnContext - The context for the current turn of the conversation. * @returns A promise that resolves to true if a handler was executed, false otherwise. * * @remarks * This is the core internal method that processes a turn in the conversation. * It handles routing and executing handlers based on the activity type and content. * While this method is public, it's typically called internally by the `run` method. * * The method performs the following operations: * 1. Starts typing timer if configured * 2. Processes mentions if configured * 3. Loads turn state * 4. Handles authentication flows * 5. Executes before-turn event handlers * 6. Downloads files if file downloaders are configured * 7. Routes to appropriate handlers * 8. Executes after-turn event handlers * 9. Saves turn state * * @example * ```typescript * const handled = await app.runInternal(turnContext); * if (!handled) { * console.log('No handler matched the activity'); * } * ``` */ runInternal(turnContext: TurnContext): Promise<boolean>; /** * Sends a proactive message to a conversation. * * @param context - The turn context or conversation reference to use. * @param activityOrText - The activity or text to send. * @param speak - Optional text to be spoken by the bot on a speech-enabled channel. * @param inputHint - Optional input hint for the activity. * @returns A promise that resolves to the resource response from sending the activity. * * @remarks * This method allows you to send messages proactively to a conversation, outside the normal turn flow. * * @example * ```typescript * // With conversation reference * await app.sendProactiveActivity(conversationReference, 'Important notification!'); * * // From an existing context * await app.sendProactiveActivity(turnContext, 'Important notification!'); * ``` */ sendProactiveActivity(context: TurnContext | ConversationReference, activityOrText: string | Activity, speak?: string, inputHint?: string): Promise<ResourceResponse | undefined>; /** * Starts a typing indicator timer for the current turn context. * * @param context - The turn context for the current conversation. * @returns void * * @remarks * This method starts a timer that sends typing activity indicators to the user * at regular intervals. The typing indicator continues until a message is sent * or the timer is explicitly stopped. * * The typing indicator helps provide feedback to users that the agent is processing * their message, especially when responses might take time to generate. * * @example * ```typescript * app.startTypingTimer(turnContext); * // Do some processing... * await turnContext.sendActivity('Response after processing'); * // Typing timer automatically stops when sending a message * ``` */ startTypingTimer(context: TurnContext): void; /** * Registers an extension with the application. * * @typeParam T - The extension type extending AgentExtension. * @param extension - The extension instance to register. * @param regcb - Callback function called after successful registration. * @throws Error if the extension is already registered. * * @remarks * Extensions provide a way to add custom functionality to the application. * Each extension can only be registered once to prevent conflicts. * * @example * ```typescript * const myExtension = new MyCustomExtension(); * app.registerExtension(myExtension, (ext) => { * console.log('Extension registered:', ext.name); * }); * ``` */ registerExtension<T extends AgentExtension<TState>>(extension: T, regcb: (ext: T) => void): void; /** * Stops the typing indicator timer if it's currently running. * * @returns void * * @remarks * This method clears the typing indicator timer to prevent further typing indicators * from being sent. It's typically called automatically when a message is sent, but * can also be called manually to stop the typing indicator. * * @example * ```typescript * app.startTypingTimer(turnContext); * // Do some processing... * app.stopTypingTimer(); // Manually stop the typing indicator * ``` */ stopTypingTimer(): void; /** * Adds an event handler for specified turn events. * * @param event - The turn event(s) to handle. Can be 'beforeTurn', 'afterTurn', or other custom events. * @param handler - The handler function that will be called when the event occurs. * @returns The current instance of the application. * * @remarks * Turn events allow you to execute logic before or after the main turn processing. * Handlers added for 'beforeTurn' are executed before routing logic. * Handlers added for 'afterTurn' are executed after routing logic. * * @example * ```typescript * app.onTurn('beforeTurn', async (context, state) => { * console.log('Processing before turn'); * return true; // Continue execution * }); * ``` */ onTurn(event: TurnEvents | TurnEvents[], handler: (context: TurnContext, state: TState) => Promise<boolean>): this; /** * Calls a series of event handlers in sequence. * * @param context - The turn context for the current conversation. * @param state - The current turn state. * @param handlers - Array of event handlers to call. * @returns A promise that resolves to true if all handlers returned true, false otherwise. */ protected callEventHandlers(context: TurnContext, state: TState, handlers: ApplicationEventHandler<TState>[]): Promise<boolean>; /** * Starts a long-running call, potentially in a new conversation context. * * @param context - The turn context for the current conversation. * @param handler - The handler function to execute. * @returns A promise that resolves to the result of the handler. */ protected startLongRunningCall(context: TurnContext, handler: (context: TurnContext) => Promise<boolean>): Promise<boolean>; /** * Creates a selector function for activity types. * * @param type - The activity type to match. Can be a string, RegExp, or RouteSelector function. * @returns A RouteSelector function that matches the specified activity type. */ private createActivitySelector; /** * Creates a selector function for conversation update events. * * @param event - The conversation update event to match. * @returns A RouteSelector function that matches the specified conversation update event. */ private createConversationUpdateSelector; /** * Creates a selector function for message content matching. * * @param keyword - The keyword, pattern, or selector function to match against message text. * @returns A RouteSelector function that matches messages based on the specified keyword. */ private createMessageSelector; }