@microsoft/agents-activity
Version:
Microsoft 365 Agents SDK for JavaScript. Activity Protocol serialization and deserialization.
343 lines • 15.3 kB
JavaScript
"use strict";
/**
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License.
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.Activity = exports.activityZodSchema = void 0;
const uuid_1 = require("uuid");
const zod_1 = require("zod");
const semanticAction_1 = require("./action/semanticAction");
const suggestedActions_1 = require("./action/suggestedActions");
const activityEventNames_1 = require("./activityEventNames");
const activityImportance_1 = require("./activityImportance");
const activityTypes_1 = require("./activityTypes");
const attachment_1 = require("./attachment/attachment");
const attachmentLayoutTypes_1 = require("./attachment/attachmentLayoutTypes");
const channelAccount_1 = require("./conversation/channelAccount");
const channels_1 = require("./conversation/channels");
const conversationAccount_1 = require("./conversation/conversationAccount");
const conversationReference_1 = require("./conversation/conversationReference");
const endOfConversationCodes_1 = require("./conversation/endOfConversationCodes");
const deliveryModes_1 = require("./deliveryModes");
const entity_1 = require("./entity/entity");
const inputHints_1 = require("./inputHints");
const messageReaction_1 = require("./messageReaction");
const textFormatTypes_1 = require("./textFormatTypes");
const textHighlight_1 = require("./textHighlight");
/**
* Zod schema for validating an Activity object.
*/
exports.activityZodSchema = zod_1.z.object({
type: zod_1.z.union([activityTypes_1.activityTypesZodSchema, zod_1.z.string().min(1)]),
text: zod_1.z.string().optional(),
id: zod_1.z.string().min(1).optional(),
channelId: zod_1.z.string().min(1).optional(),
from: channelAccount_1.channelAccountZodSchema.optional(),
timestamp: zod_1.z.union([zod_1.z.date(), zod_1.z.string().min(1).datetime().optional(), zod_1.z.string().min(1).transform(s => new Date(s)).optional()]),
localTimestamp: zod_1.z.string().min(1).transform(s => new Date(s)).optional().or(zod_1.z.date()).optional(), // z.string().min(1).transform(s => new Date(s)).optional(),
localTimezone: zod_1.z.string().min(1).optional(),
callerId: zod_1.z.string().min(1).optional(),
serviceUrl: zod_1.z.string().min(1).optional(),
conversation: conversationAccount_1.conversationAccountZodSchema.optional(),
recipient: channelAccount_1.channelAccountZodSchema.optional(),
textFormat: zod_1.z.union([textFormatTypes_1.textFormatTypesZodSchema, zod_1.z.string().min(1)]).optional(),
attachmentLayout: zod_1.z.union([attachmentLayoutTypes_1.attachmentLayoutTypesZodSchema, zod_1.z.string().min(1)]).optional(),
membersAdded: zod_1.z.array(channelAccount_1.channelAccountZodSchema).optional(),
membersRemoved: zod_1.z.array(channelAccount_1.channelAccountZodSchema).optional(),
reactionsAdded: zod_1.z.array(messageReaction_1.messageReactionZodSchema).optional(),
reactionsRemoved: zod_1.z.array(messageReaction_1.messageReactionZodSchema).optional(),
topicName: zod_1.z.string().min(1).optional(),
historyDisclosed: zod_1.z.boolean().optional(),
locale: zod_1.z.string().min(1).optional(),
speak: zod_1.z.string().min(1).optional(),
inputHint: zod_1.z.union([inputHints_1.inputHintsZodSchema, zod_1.z.string().min(1)]).optional(),
summary: zod_1.z.string().min(1).optional(),
suggestedActions: suggestedActions_1.suggestedActionsZodSchema.optional(),
attachments: zod_1.z.array(attachment_1.attachmentZodSchema).optional(),
entities: zod_1.z.array(entity_1.entityZodSchema.passthrough()).optional(),
channelData: zod_1.z.any().optional(),
action: zod_1.z.string().min(1).optional(),
replyToId: zod_1.z.string().min(1).optional(),
label: zod_1.z.string().min(1).optional(),
valueType: zod_1.z.string().min(1).optional(),
value: zod_1.z.unknown().optional(),
name: zod_1.z.union([activityEventNames_1.activityEventNamesZodSchema, zod_1.z.string().min(1)]).optional(),
relatesTo: conversationReference_1.conversationReferenceZodSchema.optional(),
code: zod_1.z.union([endOfConversationCodes_1.endOfConversationCodesZodSchema, zod_1.z.string().min(1)]).optional(),
expiration: zod_1.z.string().min(1).datetime().optional(),
importance: zod_1.z.union([activityImportance_1.activityImportanceZodSchema, zod_1.z.string().min(1)]).optional(),
deliveryMode: zod_1.z.union([deliveryModes_1.deliveryModesZodSchema, zod_1.z.string().min(1)]).optional(),
listenFor: zod_1.z.array(zod_1.z.string().min(1)).optional(),
textHighlights: zod_1.z.array(textHighlight_1.textHighlightZodSchema).optional(),
semanticAction: semanticAction_1.semanticActionZodSchema.optional()
});
/**
* Represents an activity in a conversation.
*/
class Activity {
/**
* Creates a new Activity instance.
* @param t The type of the activity.
* @throws Will throw an error if the activity type is invalid.
*/
constructor(t) {
if (t === undefined) {
throw new Error('Invalid ActivityType: undefined');
}
if (t === null) {
throw new Error('Invalid ActivityType: null');
}
if ((typeof t === 'string') && (t.length === 0)) {
throw new Error('Invalid ActivityType: empty string');
}
this.type = t;
}
/**
* Creates an Activity instance from a JSON string.
* @param json The JSON string representing the activity.
* @returns The created Activity instance.
*/
static fromJson(json) {
return this.fromObject(JSON.parse(json));
}
/**
* Creates an Activity instance from an object.
* @param o The object representing the activity.
* @returns The created Activity instance.
*/
static fromObject(o) {
const parsedActivity = exports.activityZodSchema.passthrough().parse(o);
const activity = new Activity(parsedActivity.type);
Object.assign(activity, parsedActivity);
return activity;
}
/**
* Creates a continuation activity from a conversation reference.
* @param reference The conversation reference.
* @returns The created continuation activity.
*/
static getContinuationActivity(reference) {
const continuationActivityObj = {
type: activityTypes_1.ActivityTypes.Event,
name: activityEventNames_1.ActivityEventNames.ContinueConversation,
id: (0, uuid_1.v4)(),
channelId: reference.channelId,
locale: reference.locale,
serviceUrl: reference.serviceUrl,
conversation: reference.conversation,
recipient: reference.agent,
from: reference.user,
relatesTo: reference
};
const continuationActivity = Activity.fromObject(continuationActivityObj);
return continuationActivity;
}
/**
* Gets the appropriate reply-to ID for the activity.
* @returns The reply-to ID, or undefined if not applicable.
*/
getAppropriateReplyToId() {
if (this.type !== activityTypes_1.ActivityTypes.ConversationUpdate ||
(this.channelId !== channels_1.Channels.Directline && this.channelId !== channels_1.Channels.Webchat)) {
return this.id;
}
return undefined;
}
/**
* Gets the conversation reference for the activity.
* @returns The conversation reference.
* @throws Will throw an error if required properties are undefined.
*/
getConversationReference() {
if (this.recipient === null || this.recipient === undefined) {
throw new Error('Activity Recipient undefined');
}
if (this.conversation === null || this.conversation === undefined) {
throw new Error('Activity Conversation undefined');
}
if (this.channelId === null || this.channelId === undefined) {
throw new Error('Activity ChannelId undefined');
}
return {
activityId: this.getAppropriateReplyToId(),
user: this.from,
agent: this.recipient,
conversation: this.conversation,
channelId: this.channelId,
locale: this.locale,
serviceUrl: this.serviceUrl
};
}
/**
* Applies a conversation reference to the activity.
* @param reference The conversation reference.
* @param isIncoming Whether the activity is incoming.
* @returns The updated activity.
*/
applyConversationReference(reference, isIncoming = false) {
var _a, _b, _c;
this.channelId = reference.channelId;
(_a = this.locale) !== null && _a !== void 0 ? _a : (this.locale = reference.locale);
this.serviceUrl = reference.serviceUrl;
this.conversation = reference.conversation;
if (isIncoming) {
this.from = reference.user;
this.recipient = (_b = reference.agent) !== null && _b !== void 0 ? _b : undefined;
if (reference.activityId) {
this.id = reference.activityId;
}
}
else {
this.from = (_c = reference.agent) !== null && _c !== void 0 ? _c : undefined;
this.recipient = reference.user;
if (reference.activityId) {
this.replyToId = reference.activityId;
}
}
return this;
}
clone() {
const activityCopy = JSON.parse(JSON.stringify(this));
for (const key in activityCopy) {
if (typeof activityCopy[key] === 'string' && !isNaN(Date.parse(activityCopy[key]))) {
activityCopy[key] = new Date(activityCopy[key]);
}
}
Object.setPrototypeOf(activityCopy, Activity.prototype);
return activityCopy;
}
/**
* Gets the mentions in the activity.
* @param activity The activity.
* @returns The list of mentions.
*/
getMentions(activity) {
const result = [];
if (activity.entities !== undefined) {
for (let i = 0; i < activity.entities.length; i++) {
if (activity.entities[i].type.toLowerCase() === 'mention') {
result.push(activity.entities[i]);
}
}
}
return result;
}
/**
* Normalizes mentions in the activity by removing mention tags and optionally removing recipient mention.
* @param removeMention Whether to remove the recipient mention from the activity.
*/
normalizeMentions(removeMention = false) {
var _a, _b;
if (this.type === activityTypes_1.ActivityTypes.Message) {
if (removeMention) {
// Strip recipient mention tags and text
this.removeRecipientMention();
// Strip entity.mention records for recipient id
if (this.entities !== undefined && ((_a = this.recipient) === null || _a === void 0 ? void 0 : _a.id)) {
this.entities = this.entities.filter((entity) => {
var _a;
if (entity.type.toLowerCase() === 'mention') {
const mention = entity;
return mention.mentioned.id !== ((_a = this.recipient) === null || _a === void 0 ? void 0 : _a.id);
}
return true;
});
}
}
// Remove <at> </at> tags keeping the inner text
if (this.text) {
this.text = Activity.removeAt(this.text);
}
// Remove <at> </at> tags from mention records keeping the inner text
if (this.entities !== undefined) {
const mentions = this.getMentions(this);
for (const mention of mentions) {
if (mention.text) {
mention.text = (_b = Activity.removeAt(mention.text)) === null || _b === void 0 ? void 0 : _b.trim();
}
}
}
}
}
/**
* Removes <at> </at> tags from the specified text.
* @param text The text to process.
* @returns The text with <at> </at> tags removed.
*/
static removeAt(text) {
if (!text) {
return text;
}
let foundTag;
do {
foundTag = false;
const iAtStart = text.toLowerCase().indexOf('<at');
if (iAtStart >= 0) {
const iAtEnd = text.indexOf('>', iAtStart);
if (iAtEnd > 0) {
const iAtClose = text.toLowerCase().indexOf('</at>', iAtEnd);
if (iAtClose > 0) {
// Replace </at>
let followingText = text.substring(iAtClose + 5);
// If first char of followingText is not whitespace, insert space
if (followingText.length > 0 && !(/\s/.test(followingText[0]))) {
followingText = ` ${followingText}`;
}
text = text.substring(0, iAtClose) + followingText;
// Get tag content (text between <at...> and </at>)
const tagContent = text.substring(iAtEnd + 1, iAtClose);
// Replace <at ...> with just the tag content
let prefixText = text.substring(0, iAtStart);
// If prefixText is not empty and doesn't end with whitespace, add a space
if (prefixText.length > 0 && !(/\s$/.test(prefixText))) {
prefixText += ' ';
}
text = prefixText + tagContent + followingText;
// We found one, try again, there may be more
foundTag = true;
}
}
}
} while (foundTag);
return text;
}
/**
* Removes the mention text for a given ID.
* @param id The ID of the mention to remove.
* @returns The updated text.
*/
removeMentionText(id) {
const mentions = this.getMentions(this);
const mentionsFiltered = mentions.filter((mention) => mention.mentioned.id === id);
if ((mentionsFiltered.length > 0) && this.text) {
this.text = this.text.replace(mentionsFiltered[0].text, '').trim();
}
return this.text || '';
}
/**
* Removes the recipient mention from the activity text.
* @returns The updated text.
*/
removeRecipientMention() {
if ((this.recipient != null) && this.recipient.id) {
return this.removeMentionText(this.recipient.id);
}
return '';
}
/**
* Gets the conversation reference for a reply.
* @param replyId The ID of the reply.
* @returns The conversation reference.
*/
getReplyConversationReference(replyId) {
const reference = this.getConversationReference();
reference.activityId = replyId;
return reference;
}
toJsonString() {
return JSON.stringify(this);
}
}
exports.Activity = Activity;
//# sourceMappingURL=activity.js.map