UNPKG

@microsoft/teams.apps

Version:

<p> <a href="https://www.npmjs.com/package/@microsoft/teams.apps" target="_blank"> <img src="https://img.shields.io/npm/v/@microsoft/teams.apps/latest" /> </a> <a href="https://www.npmjs.com/package/@microsoft/teams.apps?activeTab=code

387 lines • 32.8 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.ActivityProcessor = void 0; const teams_api_1 = require("@microsoft/teams.api"); const activity_sender_1 = require("./activity-sender"); const api_1 = require("./api"); const contexts_1 = require("./contexts"); const agent365_baggage_1 = require("./diagnostics/agent365-baggage"); const constants_1 = require("./diagnostics/constants"); const helpers_1 = require("./diagnostics/helpers"); const files_credential_1 = require("./files-credential"); const utils_1 = require("./utils"); function getAgenticIdentity(account) { if (!account?.agenticAppBlueprintId) { return undefined; } return { agenticAppId: account.agenticAppId, agenticUserId: account.agenticUserId, tenantId: account.tenantId, agenticAppBlueprintId: account.agenticAppBlueprintId, }; } /** * Encapsulates inbound activity processing: building the activity context, * running the middleware/route chain, wiring streaming + send events, and * producing the {@link InvokeResponse}. * * This replaces the `this: App`-bound `$process` free function. All of its * collaborators are supplied via the constructor so the processor can be * reasoned about (and tested) without reaching into `App` internals. */ class ActivityProcessor { options; constructor(options) { this.options = options; } /** * activity handler called when an inbound activity is received * @param event the received activity event */ async process(event) { const { token, body } = event; if (!body) { throw new Error('Activity body is required'); } // TODO: We currently simply cast the models to Activity, // but we should probably be validating this conversion const activity = body; this.options.log.debug(`activity/${activity.type}${activity.type === 'invoke' ? `/${activity.name}` : ''}`); let serviceUrl = activity.serviceUrl || token.serviceUrl; if (serviceUrl.endsWith('/')) { serviceUrl = serviceUrl.slice(0, serviceUrl.length - 1); } // Establish Agent365 baggage before any span starts, so every span in the // turn observes the same identity. A route-level middleware runs too late: // the root span already exists by then. return this.withActivityBaggage(activity, () => traceActivityProcess(activity, serviceUrl, async (activityProcessSpan) => { const agenticIdentity = getAgenticIdentity(activity.recipient); const apiClient = this.options.api.clone({ serviceUrl, agenticIdentity, }); let userToken; // Legacy context OAuth fields opt into this eager lookup. if (this.options.shouldFetchUserToken()) { try { userToken = await this.getUserToken(apiClient, activity.channelId, activity.from.id); } catch (err) { // noop } } const client = this.options.client.clone(); const apiClientFactory = (senderServiceUrl, senderAgenticIdentity) => apiClient.clone({ serviceUrl: senderServiceUrl, agenticIdentity: senderAgenticIdentity ?? agenticIdentity, }); const userGraph = new api_1.GraphClient(client.clone({ token: () => userToken }), { baseUrlRoot: this.options.graphBaseUrl }); const appGraph = new api_1.GraphClient(client.clone({ // The token provider returns null when the app has no credentials, but the HTTP token contract treats only undefined as "no token"; coerce so null is never forwarded as an auth header. token: async () => (await this.options.getAppGraphToken((0, utils_1.extractTenantId)(activity))) ?? undefined, }), { baseUrlRoot: this.options.graphBaseUrl }); const ref = { serviceUrl, activityId: activity.id, bot: activity.recipient, channelId: activity.channelId, conversation: activity.conversation, locale: activity.locale, user: activity.from, }; const routes = this.options.router.selectRoutes(activity); if (routes.length === 0) { (0, helpers_1.recordTeamsBotHandlerUnmatched)(activity.type, getInvokeName(activity)); } // Collect plugin contexts BEFORE creating the activity context let pluginContexts = {}; for (let i = this.options.plugins.length - 1; i > -1; i--) { const plugin = this.options.plugins[i]; if (plugin.onActivity) { const additionalPluginContext = await plugin.onActivity({ ...ref, activity, token, }); if (additionalPluginContext) { for (const key in additionalPluginContext) { if (key in pluginContexts) { this.options.log.warn(`Plugin context key "${key}" already exists. Overriding.`); } } pluginContexts = { ...pluginContexts, ...additionalPluginContext, }; } } } let i = -1; let data = undefined; const next = async (ctx) => { if (i === routes.length - 1) return data; i++; const mergedContext = ctx || { ...context.toInterface(), ...pluginContexts, }; const route = routes[i]; const handler = route.callback; const res = await traceHandler(activity, route, async () => handler(mergedContext)); if (res) { data = res; } return data; }; const activitySender = new activity_sender_1.ActivitySender(this.options.log, apiClientFactory); // Resolved at fetch time rather than eagerly, so a turn that never touches files pays nothing for it. // // An Agentic User reads as itself. An app-only token sees what the app may read tenant-wide, a different set from what was shared with the agent, so it would 403 on exactly the files the agent was given. const filesCredential = (0, files_credential_1.selectFilesCredential)({ agenticIdentity, tenantId: (0, utils_1.extractTenantId)(activity), graphBaseUrlRoot: this.options.graphBaseUrl, getAppGraphToken: (t) => this.options.getAppGraphToken(t), getAgenticGraphToken: (i, t) => this.options.getAgenticGraphToken(i, t), }); // Inline images are read with the turn's Bot API token, not Graph. const filesBotApiCredential = (0, files_credential_1.selectInlineImageCredential)({ agenticIdentity, serviceUrl: activity.serviceUrl, getToken: () => apiClient.http.token, }); const context = new contexts_1.ActivityContext({ activity, next, api: apiClient, client: this.options.client, userGraph, appGraph, appId: this.options.getId() || '', log: this.options.log, userToken: userToken, ref, storage: this.options.storage, isSignedIn: !!userToken, connectionName: this.options.getConnectionName(), validateOAuthConnection: this.options.validateOAuthConnection, onOAuthSignInInitiated: this.options.onOAuthSignInInitiated, getOAuthConnectionStatus: this.options.getOAuthConnectionStatus, filesCredential, filesBotApiCredential, activitySender, ...pluginContexts }); const conversationId = activity.conversation?.id; const userId = activity.from?.id; const stateLoader = this.options.getStateLoader(); const send = context.send.bind(context); context.send = async (activity, conversationRef) => { const res = conversationRef ? await send(activity, conversationRef) : await send(activity); this.options.eventManager.onActivitySent({ ...(conversationRef ?? ref), activity: res, }); return res; }; let planAttached = false; const getPlan = context.plan.bind(context); context.plan = () => { const plan = getPlan(); if (!planAttached) { const planRef = structuredClone(context.ref); plan.events.on('sent', (activity) => { this.options.eventManager.onActivitySent({ ...structuredClone(planRef), activity }); }); planAttached = true; } return plan; }; context.stream.events.on('chunk', (activity) => { this.options.eventManager.onActivitySent({ ...ref, activity, }); }); context.stream.events.on('close', (activity) => { this.options.eventManager.onActivitySent({ ...ref, activity, }); }); const response = await this.executeTurnLifecycle(context, activity, activityProcessSpan, conversationId, userId, stateLoader, async () => { const res = await next(); await context.stream.close(); return (0, teams_api_1.isInvokeResponse)(res) ? res : { status: 200, body: res }; }); this.options.eventManager.onActivityResponse({ ...ref, activity, response, }); return response; })); } async executeTurnLifecycle(context, activity, activityProcessSpan, conversationId, userId, stateLoader, dispatch) { let response = { status: 500 }; let dispatchError; let saveError; try { if (stateLoader && conversationId) { context.state = await stateLoader.load(conversationId, userId); } response = await dispatch(); } catch (error) { dispatchError = error; } finally { if (context.state && stateLoader && conversationId) { try { await stateLoader.save(context.state); } catch (error) { saveError = error; } finally { context.state.seal(); } } } if (dispatchError) { await this.reportTurnError(dispatchError, activity, activityProcessSpan); } if (saveError) { await this.reportTurnError(saveError, activity, activityProcessSpan); } if (dispatchError || saveError) { return { status: 500 }; } return response; } async reportTurnError(error, activity, activityProcessSpan) { (0, helpers_1.recordTeamsBotApplicationException)(activityProcessSpan, error); await this.options.eventManager.onError({ error: error instanceof Error ? error : new Error(String(error)), activity, }); } /** * Runs the inbound turn inside an Agent365 baggage scope derived from the * activity, unless the bridge is disabled. */ withActivityBaggage(activity, execute) { const options = this.options.agent365Baggage; if (options === false) { return execute(); } return (0, agent365_baggage_1.withAgent365Baggage)((0, agent365_baggage_1.agent365BaggageFromActivity)(activity, options ?? {}), execute); } /** * fetch the user's token for the given channel/user, if signed in */ async getUserToken(apiClient, channelId, userId) { const res = await apiClient.users.getToken({ channelId, userId, connectionName: this.options.getConnectionName(), }); return res.token; } } exports.ActivityProcessor = ActivityProcessor; function getInvokeName(activity) { return activity.type === 'invoke' ? activity.name : undefined; } function getHandlerType(activity) { return getInvokeName(activity) ?? activity.type; } function getHandlerDispatch(activity, route) { if (!route.name) { return constants_1.APP_HANDLER_DISPATCH.catchall; } if (route.name === 'activity') { return constants_1.APP_HANDLER_DISPATCH.catchall; } if (route.name === activity.type) { return constants_1.APP_HANDLER_DISPATCH.type; } if (activity.type === 'invoke') { return constants_1.APP_HANDLER_DISPATCH.invoke; } return constants_1.APP_HANDLER_DISPATCH.catchall; } function getActivityProcessAttributes(activity, serviceUrl) { const attributes = { [constants_1.APP_ATTRIBUTE_NAMES.activityType]: activity.type, }; if (activity.id) { attributes[constants_1.APP_ATTRIBUTE_NAMES.activityId] = activity.id; } if (activity.conversation?.id) { attributes[constants_1.APP_ATTRIBUTE_NAMES.conversationId] = activity.conversation.id; } if (activity.channelId) { attributes[constants_1.APP_ATTRIBUTE_NAMES.channelId] = activity.channelId; } if (activity.recipient?.id) { attributes[constants_1.APP_ATTRIBUTE_NAMES.botId] = activity.recipient.id; } if (serviceUrl) { attributes[constants_1.APP_ATTRIBUTE_NAMES.serviceUrl] = serviceUrl; } return attributes; } async function traceActivityProcess(activity, serviceUrl, execute) { const activityType = activity.type; const startedAt = Date.now(); (0, helpers_1.recordTeamsBotActivityReceived)(activityType); return (0, helpers_1.getTeamsBotApplicationTracer)().startActiveSpan(constants_1.APP_SPAN_NAMES.activityProcess, { attributes: getActivityProcessAttributes(activity, serviceUrl) }, async (span) => { try { return await execute(span); } catch (error) { (0, helpers_1.recordTeamsBotApplicationException)(span, error); throw error; } finally { (0, helpers_1.recordTeamsBotActivityProcessDuration)(activityType, Date.now() - startedAt); span.end(); } }); } async function traceHandler(activity, route, execute) { if (route.type === 'system') { return execute(); } const handlerType = getHandlerType(activity); const handlerDispatch = getHandlerDispatch(activity, route); const startedAt = Date.now(); (0, helpers_1.recordTeamsBotHandlerDispatched)(handlerType, handlerDispatch); return (0, helpers_1.getTeamsBotApplicationTracer)().startActiveSpan(constants_1.APP_SPAN_NAMES.handler, { attributes: { [constants_1.APP_ATTRIBUTE_NAMES.handlerType]: handlerType, [constants_1.APP_ATTRIBUTE_NAMES.handlerDispatch]: handlerDispatch, }, }, async (span) => { try { return await execute(); } catch (error) { (0, helpers_1.recordTeamsBotHandlerFailure)(handlerType, handlerDispatch); (0, helpers_1.recordTeamsBotApplicationException)(span, error); throw error; } finally { (0, helpers_1.recordTeamsBotHandlerDuration)(handlerType, handlerDispatch, Date.now() - startedAt); span.end(); } }); } //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiYXBwLnByb2Nlc3MuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi9zcmMvYXBwLnByb2Nlc3MudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7O0FBRUEsb0RBYThCO0FBRzlCLHVEQUFtRDtBQUNuRCwrQkFBb0Q7QUFFcEQseUNBQStEO0FBQy9ELHFFQUl3QztBQUN4Qyx1REFBb0c7QUFDcEcsbURBUytCO0FBRS9CLHlEQUF3RjtBQU94RixtQ0FBMEM7QUFFMUMsU0FBUyxrQkFBa0IsQ0FBQyxPQUFpQjtJQUMzQyxJQUFJLENBQUMsT0FBTyxFQUFFLHFCQUFxQixFQUFFLENBQUM7UUFDcEMsT0FBTyxTQUFTLENBQUM7SUFDbkIsQ0FBQztJQUVELE9BQU87UUFDTCxZQUFZLEVBQUUsT0FBTyxDQUFDLFlBQVk7UUFDbEMsYUFBYSxFQUFFLE9BQU8sQ0FBQyxhQUFhO1FBQ3BDLFFBQVEsRUFBRSxPQUFPLENBQUMsUUFBUTtRQUMxQixxQkFBcUIsRUFBRSxPQUFPLENBQUMscUJBQXFCO0tBQ3JELENBQUM7QUFDSixDQUFDO0FBMkVEOzs7Ozs7OztHQVFHO0FBQ0gsTUFBYSxpQkFBaUI7SUFDQztJQUE3QixZQUE2QixPQUEyQztRQUEzQyxZQUFPLEdBQVAsT0FBTyxDQUFvQztJQUFJLENBQUM7SUFFN0U7OztPQUdHO0lBQ0gsS0FBSyxDQUFDLE9BQU8sQ0FBQyxLQUFxQjtRQUNqQyxNQUFNLEVBQUUsS0FBSyxFQUFFLElBQUksRUFBRSxHQUFHLEtBQUssQ0FBQztRQUU5QixJQUFJLENBQUMsSUFBSSxFQUFFLENBQUM7WUFDVixNQUFNLElBQUksS0FBSyxDQUFDLDJCQUEyQixDQUFDLENBQUM7UUFDL0MsQ0FBQztRQUVELHlEQUF5RDtRQUN6RCx1REFBdUQ7UUFDdkQsTUFBTSxRQUFRLEdBQUcsSUFBZ0IsQ0FBQztRQUVsQyxJQUFJLENBQUMsT0FBTyxDQUFDLEdBQUcsQ0FBQyxLQUFLLENBQ3BCLFlBQVksUUFBUSxDQUFDLElBQUksR0FBRyxRQUFR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