diff --git a/core/src/Microsoft.Teams.Apps/Handlers/AgentLifecycleHandler.Activity.cs b/core/src/Microsoft.Teams.Apps/Handlers/AgentLifecycleHandler.Activity.cs
new file mode 100644
index 00000000..9be3d6bf
--- /dev/null
+++ b/core/src/Microsoft.Teams.Apps/Handlers/AgentLifecycleHandler.Activity.cs
@@ -0,0 +1,338 @@
+// Copyright (c) Microsoft Corporation.
+// Licensed under the MIT License.
+
+using System.Text.Json;
+using System.Text.Json.Serialization;
+
+namespace Microsoft.Teams.Apps.Handlers;
+
+///
+/// Represents an Agent 365 agentLifecycle event activity.
+///
+public class AgentLifecycleEventActivity : EventActivity
+{
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ [JsonConstructor]
+ internal AgentLifecycleEventActivity() : base()
+ {
+ Name = EventNames.AgentLifecycle;
+ }
+
+ ///
+ /// Initializes a new instance of the class from an event activity.
+ ///
+ /// The source event activity.
+ internal AgentLifecycleEventActivity(EventActivity activity) : base(activity)
+ {
+ Name ??= EventNames.AgentLifecycle;
+ }
+
+ ///
+ /// Creates the most-specific Agent 365 lifecycle activity type for the supplied event activity.
+ ///
+ /// The source event activity.
+ /// A typed lifecycle event activity when the valueType is known; otherwise a base lifecycle activity.
+ internal static AgentLifecycleEventActivity FromEventActivity(EventActivity activity)
+ {
+ ArgumentNullException.ThrowIfNull(activity);
+
+ return activity.ValueType switch
+ {
+ AgentLifecycleEventValueTypes.AgenticUserIdentityCreated => new AgenticUserIdentityCreatedActivity(activity),
+ AgentLifecycleEventValueTypes.AgenticUserIdentityUpdated => new AgenticUserIdentityUpdatedActivity(activity),
+ AgentLifecycleEventValueTypes.AgenticUserManagerUpdated => new AgenticUserManagerUpdatedActivity(activity),
+ AgentLifecycleEventValueTypes.AgenticUserEnabled => new AgenticUserEnabledActivity(activity),
+ AgentLifecycleEventValueTypes.AgenticUserDisabled => new AgenticUserDisabledActivity(activity),
+ AgentLifecycleEventValueTypes.AgenticUserDeleted => new AgenticUserDeletedActivity(activity),
+ AgentLifecycleEventValueTypes.AgenticUserUndeleted => new AgenticUserUndeletedActivity(activity),
+ AgentLifecycleEventValueTypes.AgenticUserWorkloadOnboardingUpdated => new AgenticUserWorkloadOnboardingUpdatedActivity(activity),
+ _ => new AgentLifecycleEventActivity(activity),
+ };
+ }
+}
+
+///
+/// Represents an Agent 365 agentLifecycle event activity with a strongly-typed value payload.
+///
+/// The lifecycle event value payload type.
+public class AgentLifecycleEventActivity : AgentLifecycleEventActivity
+{
+ ///
+ /// Gets or sets the strongly-typed value associated with the lifecycle event activity.
+ ///
+ [JsonPropertyName("value")]
+ public new TValue? Value
+ {
+ get => base.Value != null ? JsonSerializer.Deserialize(base.Value.ToJsonString()) : default;
+ set => base.Value = value != null ? JsonSerializer.SerializeToNode(value) : null;
+ }
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ internal AgentLifecycleEventActivity() : base()
+ {
+ }
+
+ ///
+ /// Initializes a new instance of the class from an event activity.
+ ///
+ /// The source event activity.
+ internal AgentLifecycleEventActivity(EventActivity activity) : base(activity)
+ {
+ }
+}
+
+///
+/// Fired when an agentic user identity is created.
+///
+public class AgenticUserIdentityCreatedActivity : AgentLifecycleEventActivity
+{
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ [JsonConstructor]
+ internal AgenticUserIdentityCreatedActivity() : base()
+ {
+ ValueType = AgentLifecycleEventValueTypes.AgenticUserIdentityCreated;
+ }
+
+ ///
+ /// Initializes a new instance of the class from an event activity.
+ ///
+ /// The source event activity.
+ internal AgenticUserIdentityCreatedActivity(EventActivity activity) : base(activity)
+ {
+ ValueType ??= AgentLifecycleEventValueTypes.AgenticUserIdentityCreated;
+ }
+}
+
+///
+/// Fired when an agentic user identity property changes.
+///
+public class AgenticUserIdentityUpdatedActivity : AgentLifecycleEventActivity
+{
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ [JsonConstructor]
+ internal AgenticUserIdentityUpdatedActivity() : base()
+ {
+ ValueType = AgentLifecycleEventValueTypes.AgenticUserIdentityUpdated;
+ }
+
+ ///
+ /// Initializes a new instance of the class from an event activity.
+ ///
+ /// The source event activity.
+ internal AgenticUserIdentityUpdatedActivity(EventActivity activity) : base(activity)
+ {
+ ValueType ??= AgentLifecycleEventValueTypes.AgenticUserIdentityUpdated;
+ }
+}
+
+///
+/// Fired when an agentic user's manager changes.
+///
+public class AgenticUserManagerUpdatedActivity : AgentLifecycleEventActivity
+{
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ [JsonConstructor]
+ internal AgenticUserManagerUpdatedActivity() : base()
+ {
+ ValueType = AgentLifecycleEventValueTypes.AgenticUserManagerUpdated;
+ }
+
+ ///
+ /// Initializes a new instance of the class from an event activity.
+ ///
+ /// The source event activity.
+ internal AgenticUserManagerUpdatedActivity(EventActivity activity) : base(activity)
+ {
+ ValueType ??= AgentLifecycleEventValueTypes.AgenticUserManagerUpdated;
+ }
+}
+
+///
+/// Fired when an agentic user is enabled.
+///
+public class AgenticUserEnabledActivity : AgentLifecycleEventActivity
+{
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ [JsonConstructor]
+ internal AgenticUserEnabledActivity() : base()
+ {
+ ValueType = AgentLifecycleEventValueTypes.AgenticUserEnabled;
+ }
+
+ ///
+ /// Initializes a new instance of the class from an event activity.
+ ///
+ /// The source event activity.
+ internal AgenticUserEnabledActivity(EventActivity activity) : base(activity)
+ {
+ ValueType ??= AgentLifecycleEventValueTypes.AgenticUserEnabled;
+ }
+}
+
+///
+/// Fired when an agentic user is disabled.
+///
+public class AgenticUserDisabledActivity : AgentLifecycleEventActivity
+{
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ [JsonConstructor]
+ internal AgenticUserDisabledActivity() : base()
+ {
+ ValueType = AgentLifecycleEventValueTypes.AgenticUserDisabled;
+ }
+
+ ///
+ /// Initializes a new instance of the class from an event activity.
+ ///
+ /// The source event activity.
+ internal AgenticUserDisabledActivity(EventActivity activity) : base(activity)
+ {
+ ValueType ??= AgentLifecycleEventValueTypes.AgenticUserDisabled;
+ }
+}
+
+///
+/// Fired when an agentic user is deleted.
+///
+public class AgenticUserDeletedActivity : AgentLifecycleEventActivity
+{
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ [JsonConstructor]
+ internal AgenticUserDeletedActivity() : base()
+ {
+ ValueType = AgentLifecycleEventValueTypes.AgenticUserDeleted;
+ }
+
+ ///
+ /// Initializes a new instance of the class from an event activity.
+ ///
+ /// The source event activity.
+ internal AgenticUserDeletedActivity(EventActivity activity) : base(activity)
+ {
+ ValueType ??= AgentLifecycleEventValueTypes.AgenticUserDeleted;
+ }
+}
+
+///
+/// Fired when a previously deleted agentic user is restored.
+///
+public class AgenticUserUndeletedActivity : AgentLifecycleEventActivity
+{
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ [JsonConstructor]
+ internal AgenticUserUndeletedActivity() : base()
+ {
+ ValueType = AgentLifecycleEventValueTypes.AgenticUserUndeleted;
+ }
+
+ ///
+ /// Initializes a new instance of the class from an event activity.
+ ///
+ /// The source event activity.
+ internal AgenticUserUndeletedActivity(EventActivity activity) : base(activity)
+ {
+ ValueType ??= AgentLifecycleEventValueTypes.AgenticUserUndeleted;
+ }
+}
+
+///
+/// Fired when a workload onboarding state changes for an agentic user.
+///
+public class AgenticUserWorkloadOnboardingUpdatedActivity : AgentLifecycleEventActivity
+{
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ [JsonConstructor]
+ internal AgenticUserWorkloadOnboardingUpdatedActivity() : base()
+ {
+ ValueType = AgentLifecycleEventValueTypes.AgenticUserWorkloadOnboardingUpdated;
+ }
+
+ ///
+ /// Initializes a new instance of the class from an event activity.
+ ///
+ /// The source event activity.
+ internal AgenticUserWorkloadOnboardingUpdatedActivity(EventActivity activity) : base(activity)
+ {
+ ValueType ??= AgentLifecycleEventValueTypes.AgenticUserWorkloadOnboardingUpdated;
+ }
+}
+
+///
+/// String constants for Agent 365 lifecycle activity valueType values.
+///
+public static class AgentLifecycleEventValueTypes
+{
+ /// Agentic user identity created value type.
+ public const string AgenticUserIdentityCreated = "AgenticUserIdentityCreated";
+
+ /// Agentic user identity updated value type.
+ public const string AgenticUserIdentityUpdated = "AgenticUserIdentityUpdated";
+
+ /// Agentic user manager updated value type.
+ public const string AgenticUserManagerUpdated = "AgenticUserManagerUpdated";
+
+ /// Agentic user enabled value type.
+ public const string AgenticUserEnabled = "AgenticUserEnabled";
+
+ /// Agentic user disabled value type.
+ public const string AgenticUserDisabled = "AgenticUserDisabled";
+
+ /// Agentic user deleted value type.
+ public const string AgenticUserDeleted = "AgenticUserDeleted";
+
+ /// Agentic user undeleted value type.
+ public const string AgenticUserUndeleted = "AgenticUserUndeleted";
+
+ /// Agentic user workload onboarding updated value type.
+ public const string AgenticUserWorkloadOnboardingUpdated = "AgenticUserWorkloadOnboardingUpdated";
+}
+
+///
+/// String constants for Agent 365 lifecycle payload eventType values.
+///
+public static class AgentLifecycleEventTypes
+{
+ /// Agentic user identity created event type.
+ public const string AgenticUserIdentityCreated = "agenticUserIdentityCreated";
+
+ /// Agentic user identity updated event type.
+ public const string AgenticUserIdentityUpdated = "agenticUserIdentityUpdated";
+
+ /// Agentic user manager updated event type.
+ public const string AgenticUserManagerUpdated = "agenticUserManagerUpdated";
+
+ /// Agentic user enabled event type.
+ public const string AgenticUserEnabled = "agenticUserEnabled";
+
+ /// Agentic user disabled event type.
+ public const string AgenticUserDisabled = "agenticUserDisabled";
+
+ /// Agentic user deleted event type.
+ public const string AgenticUserDeleted = "agenticUserDeleted";
+
+ /// Agentic user undeleted event type.
+ public const string AgenticUserUndeleted = "agenticUserUndeleted";
+
+ /// Agentic user workload onboarding updated event type.
+ public const string AgenticUserWorkloadOnboardingUpdated = "agenticUserWorkloadOnboardingUpdated";
+}
diff --git a/core/src/Microsoft.Teams.Apps/Handlers/AgentLifecycleHandler.Values.cs b/core/src/Microsoft.Teams.Apps/Handlers/AgentLifecycleHandler.Values.cs
new file mode 100644
index 00000000..8838c20b
--- /dev/null
+++ b/core/src/Microsoft.Teams.Apps/Handlers/AgentLifecycleHandler.Values.cs
@@ -0,0 +1,234 @@
+// Copyright (c) Microsoft Corporation.
+// Licensed under the MIT License.
+
+using System.Text.Json.Serialization;
+
+namespace Microsoft.Teams.Apps.Handlers;
+
+///
+/// Manager profile carried by the AgenticUserIdentityCreated lifecycle event.
+///
+public class AgentLifecycleManager
+{
+ ///
+ /// Gets or sets the Entra object ID of the manager.
+ ///
+ [JsonPropertyName("userId")]
+ public string? UserId { get; set; }
+
+ ///
+ /// Gets or sets the manager's email address.
+ ///
+ [JsonPropertyName("email")]
+ public string? Email { get; set; }
+
+ ///
+ /// Gets or sets the manager's display name.
+ ///
+ [JsonPropertyName("displayName")]
+ public string? DisplayName { get; set; }
+}
+
+///
+/// Manager reference carried by the AgenticUserManagerUpdated lifecycle event.
+///
+public class AgentLifecycleManagerRef
+{
+ ///
+ /// Gets or sets the Entra object ID of the manager.
+ ///
+ [JsonPropertyName("managerId")]
+ public string? ManagerId { get; set; }
+}
+
+///
+/// A single property change carried by the AgenticUserIdentityUpdated lifecycle event.
+///
+public class AgentLifecycleUpdatedProperty
+{
+ ///
+ /// Gets or sets the name of the property that changed.
+ ///
+ [JsonPropertyName("propertyName")]
+ public required string PropertyName { get; set; }
+
+ ///
+ /// Gets or sets the new value of the property.
+ ///
+ [JsonPropertyName("propertyValue")]
+ public string? PropertyValue { get; set; }
+}
+
+///
+/// Fields shared by every Agent 365 lifecycle event payload.
+///
+public abstract class AgentLifecycleValueBase
+{
+ ///
+ /// Gets or sets the tenant that the agentic user belongs to.
+ ///
+ [JsonPropertyName("tenantId")]
+ public string? TenantId { get; set; }
+
+ ///
+ /// Gets or sets the Agent ID user-shaped identity object ID.
+ ///
+ [JsonPropertyName("agenticUserId")]
+ public string? AgenticUserId { get; set; }
+
+ ///
+ /// Gets or sets the concrete agent app instance ID.
+ ///
+ [JsonPropertyName("agenticAppInstanceId")]
+ public string? AgenticAppInstanceId { get; set; }
+
+ ///
+ /// Gets or sets the agent identity blueprint app ID.
+ ///
+ [JsonPropertyName("agentIdentityBlueprintId")]
+ public string? AgentIdentityBlueprintId { get; set; }
+
+ ///
+ /// Gets or sets the monotonic version of the agentic user state when provided by the service.
+ ///
+ [JsonPropertyName("version")]
+ public int? Version { get; set; }
+}
+
+///
+/// Payload for the AgenticUserIdentityCreated lifecycle event.
+///
+public class AgenticUserIdentityCreatedValue : AgentLifecycleValueBase
+{
+ ///
+ /// Gets or sets the lifecycle event type.
+ ///
+ [JsonPropertyName("eventType")]
+ public string EventType { get; set; } = AgentLifecycleEventTypes.AgenticUserIdentityCreated;
+
+ ///
+ /// Gets or sets the manager assigned to the agentic user at creation.
+ ///
+ [JsonPropertyName("manager")]
+ public AgentLifecycleManager? Manager { get; set; }
+
+ ///
+ /// Gets or sets when the agentic user identity expires.
+ ///
+ [JsonPropertyName("expirationDateTime")]
+ public DateTimeOffset? ExpirationDateTime { get; set; }
+}
+
+///
+/// Payload for the AgenticUserIdentityUpdated lifecycle event.
+///
+public class AgenticUserIdentityUpdatedValue : AgentLifecycleValueBase
+{
+ ///
+ /// Gets or sets the lifecycle event type.
+ ///
+ [JsonPropertyName("eventType")]
+ public string EventType { get; set; } = AgentLifecycleEventTypes.AgenticUserIdentityUpdated;
+
+ ///
+ /// Gets or sets the property that changed.
+ ///
+ [JsonPropertyName("updatedProperty")]
+ public required AgentLifecycleUpdatedProperty UpdatedProperty { get; set; }
+}
+
+///
+/// Payload for the AgenticUserManagerUpdated lifecycle event.
+///
+public class AgenticUserManagerUpdatedValue : AgentLifecycleValueBase
+{
+ ///
+ /// Gets or sets the lifecycle event type.
+ ///
+ [JsonPropertyName("eventType")]
+ public string EventType { get; set; } = AgentLifecycleEventTypes.AgenticUserManagerUpdated;
+
+ ///
+ /// Gets or sets the new manager reference. The value is absent when the manager was removed.
+ ///
+ [JsonPropertyName("manager")]
+ public AgentLifecycleManagerRef? Manager { get; set; }
+}
+
+///
+/// Payload for the AgenticUserEnabled lifecycle event.
+///
+public class AgenticUserEnabledValue : AgentLifecycleValueBase
+{
+ ///
+ /// Gets or sets the lifecycle event type.
+ ///
+ [JsonPropertyName("eventType")]
+ public string EventType { get; set; } = AgentLifecycleEventTypes.AgenticUserEnabled;
+}
+
+///
+/// Payload for the AgenticUserDisabled lifecycle event.
+///
+public class AgenticUserDisabledValue : AgentLifecycleValueBase
+{
+ ///
+ /// Gets or sets the lifecycle event type.
+ ///
+ [JsonPropertyName("eventType")]
+ public string EventType { get; set; } = AgentLifecycleEventTypes.AgenticUserDisabled;
+}
+
+///
+/// Payload for the AgenticUserDeleted lifecycle event.
+///
+public class AgenticUserDeletedValue : AgentLifecycleValueBase
+{
+ ///
+ /// Gets or sets the lifecycle event type.
+ ///
+ [JsonPropertyName("eventType")]
+ public string EventType { get; set; } = AgentLifecycleEventTypes.AgenticUserDeleted;
+
+ ///
+ /// Gets or sets the reason the agentic user was deleted.
+ ///
+ [JsonPropertyName("deletionReason")]
+ public string? DeletionReason { get; set; }
+}
+
+///
+/// Payload for the AgenticUserUndeleted lifecycle event.
+///
+public class AgenticUserUndeletedValue : AgentLifecycleValueBase
+{
+ ///
+ /// Gets or sets the lifecycle event type.
+ ///
+ [JsonPropertyName("eventType")]
+ public string EventType { get; set; } = AgentLifecycleEventTypes.AgenticUserUndeleted;
+}
+
+///
+/// Payload for the AgenticUserWorkloadOnboardingUpdated lifecycle event.
+///
+public class AgenticUserWorkloadOnboardingUpdatedValue : AgentLifecycleValueBase
+{
+ ///
+ /// Gets or sets the lifecycle event type.
+ ///
+ [JsonPropertyName("eventType")]
+ public string EventType { get; set; } = AgentLifecycleEventTypes.AgenticUserWorkloadOnboardingUpdated;
+
+ ///
+ /// Gets or sets the workload being onboarded.
+ ///
+ [JsonPropertyName("workloadName")]
+ public string? WorkloadName { get; set; }
+
+ ///
+ /// Gets or sets the onboarding state for the workload.
+ ///
+ [JsonPropertyName("workloadOnboardingState")]
+ public string? WorkloadOnboardingState { get; set; }
+}
diff --git a/core/src/Microsoft.Teams.Apps/Handlers/AgentLifecycleHandler.cs b/core/src/Microsoft.Teams.Apps/Handlers/AgentLifecycleHandler.cs
new file mode 100644
index 00000000..7c0ec862
--- /dev/null
+++ b/core/src/Microsoft.Teams.Apps/Handlers/AgentLifecycleHandler.cs
@@ -0,0 +1,262 @@
+// Copyright (c) Microsoft Corporation.
+// Licensed under the MIT License.
+
+using Microsoft.Teams.Apps.Routing;
+using Microsoft.Teams.Apps.Schema;
+
+namespace Microsoft.Teams.Apps.Handlers;
+
+///
+/// Delegate for handling all Agent 365 lifecycle event activity variants.
+///
+/// The context for the lifecycle event activity.
+/// A cancellation token that can be used to cancel the operation.
+/// A task representing the asynchronous operation.
+public delegate Task AgentLifecycleHandler(Context context, CancellationToken cancellationToken = default);
+
+///
+/// Delegate for handling agentic user identity created lifecycle event activities.
+///
+/// The context for the lifecycle event activity.
+/// A cancellation token that can be used to cancel the operation.
+/// A task representing the asynchronous operation.
+public delegate Task AgenticUserIdentityCreatedHandler(Context context, CancellationToken cancellationToken = default);
+
+///
+/// Delegate for handling agentic user identity updated lifecycle event activities.
+///
+/// The context for the lifecycle event activity.
+/// A cancellation token that can be used to cancel the operation.
+/// A task representing the asynchronous operation.
+public delegate Task AgenticUserIdentityUpdatedHandler(Context context, CancellationToken cancellationToken = default);
+
+///
+/// Delegate for handling agentic user manager updated lifecycle event activities.
+///
+/// The context for the lifecycle event activity.
+/// A cancellation token that can be used to cancel the operation.
+/// A task representing the asynchronous operation.
+public delegate Task AgenticUserManagerUpdatedHandler(Context context, CancellationToken cancellationToken = default);
+
+///
+/// Delegate for handling agentic user enabled lifecycle event activities.
+///
+/// The context for the lifecycle event activity.
+/// A cancellation token that can be used to cancel the operation.
+/// A task representing the asynchronous operation.
+public delegate Task AgenticUserEnabledHandler(Context context, CancellationToken cancellationToken = default);
+
+///
+/// Delegate for handling agentic user disabled lifecycle event activities.
+///
+/// The context for the lifecycle event activity.
+/// A cancellation token that can be used to cancel the operation.
+/// A task representing the asynchronous operation.
+public delegate Task AgenticUserDisabledHandler(Context context, CancellationToken cancellationToken = default);
+
+///
+/// Delegate for handling agentic user deleted lifecycle event activities.
+///
+/// The context for the lifecycle event activity.
+/// A cancellation token that can be used to cancel the operation.
+/// A task representing the asynchronous operation.
+public delegate Task AgenticUserDeletedHandler(Context context, CancellationToken cancellationToken = default);
+
+///
+/// Delegate for handling agentic user undeleted lifecycle event activities.
+///
+/// The context for the lifecycle event activity.
+/// A cancellation token that can be used to cancel the operation.
+/// A task representing the asynchronous operation.
+public delegate Task AgenticUserUndeletedHandler(Context context, CancellationToken cancellationToken = default);
+
+///
+/// Delegate for handling agentic user workload onboarding updated lifecycle event activities.
+///
+/// The context for the lifecycle event activity.
+/// A cancellation token that can be used to cancel the operation.
+/// A task representing the asynchronous operation.
+public delegate Task AgenticUserWorkloadOnboardingUpdatedHandler(Context context, CancellationToken cancellationToken = default);
+
+///
+/// Extension methods for registering Agent 365 lifecycle event activity handlers.
+///
+public static class AgentLifecycleExtensions
+{
+ ///
+ /// Registers a handler for all Agent 365 agentLifecycle event activity variants.
+ ///
+ /// The Teams bot application.
+ /// The handler to register.
+ /// The updated Teams bot application.
+ public static TeamsBotApplication OnAgentLifecycle(this TeamsBotApplication app, AgentLifecycleHandler handler)
+ {
+ ArgumentNullException.ThrowIfNull(handler, nameof(handler));
+ return app.RegisterAgentLifecycleRoute(
+ EventNames.AgentLifecycle,
+ valueType: null,
+ AgentLifecycleEventActivity.FromEventActivity,
+ (ctx, cancellationToken) => handler(ctx, cancellationToken));
+ }
+
+ ///
+ /// Registers a handler for AgenticUserIdentityCreated lifecycle event activities.
+ ///
+ /// The Teams bot application.
+ /// The handler to register.
+ /// The updated Teams bot application.
+ public static TeamsBotApplication OnAgenticUserIdentityCreated(this TeamsBotApplication app, AgenticUserIdentityCreatedHandler handler)
+ {
+ ArgumentNullException.ThrowIfNull(handler, nameof(handler));
+ return app.RegisterAgentLifecycleRoute(
+ AgentLifecycleEventValueTypes.AgenticUserIdentityCreated,
+ AgentLifecycleEventValueTypes.AgenticUserIdentityCreated,
+ activity => new AgenticUserIdentityCreatedActivity(activity),
+ (ctx, cancellationToken) => handler(ctx, cancellationToken));
+ }
+
+ ///
+ /// Registers a handler for AgenticUserIdentityUpdated lifecycle event activities.
+ ///
+ /// The Teams bot application.
+ /// The handler to register.
+ /// The updated Teams bot application.
+ public static TeamsBotApplication OnAgenticUserIdentityUpdated(this TeamsBotApplication app, AgenticUserIdentityUpdatedHandler handler)
+ {
+ ArgumentNullException.ThrowIfNull(handler, nameof(handler));
+ return app.RegisterAgentLifecycleRoute(
+ AgentLifecycleEventValueTypes.AgenticUserIdentityUpdated,
+ AgentLifecycleEventValueTypes.AgenticUserIdentityUpdated,
+ activity => new AgenticUserIdentityUpdatedActivity(activity),
+ (ctx, cancellationToken) => handler(ctx, cancellationToken));
+ }
+
+ ///
+ /// Registers a handler for AgenticUserManagerUpdated lifecycle event activities.
+ ///
+ /// The Teams bot application.
+ /// The handler to register.
+ /// The updated Teams bot application.
+ public static TeamsBotApplication OnAgenticUserManagerUpdated(this TeamsBotApplication app, AgenticUserManagerUpdatedHandler handler)
+ {
+ ArgumentNullException.ThrowIfNull(handler, nameof(handler));
+ return app.RegisterAgentLifecycleRoute(
+ AgentLifecycleEventValueTypes.AgenticUserManagerUpdated,
+ AgentLifecycleEventValueTypes.AgenticUserManagerUpdated,
+ activity => new AgenticUserManagerUpdatedActivity(activity),
+ (ctx, cancellationToken) => handler(ctx, cancellationToken));
+ }
+
+ ///
+ /// Registers a handler for AgenticUserEnabled lifecycle event activities.
+ ///
+ /// The Teams bot application.
+ /// The handler to register.
+ /// The updated Teams bot application.
+ public static TeamsBotApplication OnAgenticUserEnabled(this TeamsBotApplication app, AgenticUserEnabledHandler handler)
+ {
+ ArgumentNullException.ThrowIfNull(handler, nameof(handler));
+ return app.RegisterAgentLifecycleRoute(
+ AgentLifecycleEventValueTypes.AgenticUserEnabled,
+ AgentLifecycleEventValueTypes.AgenticUserEnabled,
+ activity => new AgenticUserEnabledActivity(activity),
+ (ctx, cancellationToken) => handler(ctx, cancellationToken));
+ }
+
+ ///
+ /// Registers a handler for AgenticUserDisabled lifecycle event activities.
+ ///
+ /// The Teams bot application.
+ /// The handler to register.
+ /// The updated Teams bot application.
+ public static TeamsBotApplication OnAgenticUserDisabled(this TeamsBotApplication app, AgenticUserDisabledHandler handler)
+ {
+ ArgumentNullException.ThrowIfNull(handler, nameof(handler));
+ return app.RegisterAgentLifecycleRoute(
+ AgentLifecycleEventValueTypes.AgenticUserDisabled,
+ AgentLifecycleEventValueTypes.AgenticUserDisabled,
+ activity => new AgenticUserDisabledActivity(activity),
+ (ctx, cancellationToken) => handler(ctx, cancellationToken));
+ }
+
+ ///
+ /// Registers a handler for AgenticUserDeleted lifecycle event activities.
+ ///
+ /// The Teams bot application.
+ /// The handler to register.
+ /// The updated Teams bot application.
+ public static TeamsBotApplication OnAgenticUserDeleted(this TeamsBotApplication app, AgenticUserDeletedHandler handler)
+ {
+ ArgumentNullException.ThrowIfNull(handler, nameof(handler));
+ return app.RegisterAgentLifecycleRoute(
+ AgentLifecycleEventValueTypes.AgenticUserDeleted,
+ AgentLifecycleEventValueTypes.AgenticUserDeleted,
+ activity => new AgenticUserDeletedActivity(activity),
+ (ctx, cancellationToken) => handler(ctx, cancellationToken));
+ }
+
+ ///
+ /// Registers a handler for AgenticUserUndeleted lifecycle event activities.
+ ///
+ /// The Teams bot application.
+ /// The handler to register.
+ /// The updated Teams bot application.
+ public static TeamsBotApplication OnAgenticUserUndeleted(this TeamsBotApplication app, AgenticUserUndeletedHandler handler)
+ {
+ ArgumentNullException.ThrowIfNull(handler, nameof(handler));
+ return app.RegisterAgentLifecycleRoute(
+ AgentLifecycleEventValueTypes.AgenticUserUndeleted,
+ AgentLifecycleEventValueTypes.AgenticUserUndeleted,
+ activity => new AgenticUserUndeletedActivity(activity),
+ (ctx, cancellationToken) => handler(ctx, cancellationToken));
+ }
+
+ ///
+ /// Registers a handler for AgenticUserWorkloadOnboardingUpdated lifecycle event activities.
+ ///
+ /// The Teams bot application.
+ /// The handler to register.
+ /// The updated Teams bot application.
+ public static TeamsBotApplication OnAgenticUserWorkloadOnboardingUpdated(this TeamsBotApplication app, AgenticUserWorkloadOnboardingUpdatedHandler handler)
+ {
+ ArgumentNullException.ThrowIfNull(handler, nameof(handler));
+ return app.RegisterAgentLifecycleRoute(
+ AgentLifecycleEventValueTypes.AgenticUserWorkloadOnboardingUpdated,
+ AgentLifecycleEventValueTypes.AgenticUserWorkloadOnboardingUpdated,
+ activity => new AgenticUserWorkloadOnboardingUpdatedActivity(activity),
+ (ctx, cancellationToken) => handler(ctx, cancellationToken));
+ }
+
+ private static TeamsBotApplication RegisterAgentLifecycleRoute(
+ this TeamsBotApplication app,
+ string routeSuffix,
+ string? valueType,
+ Func createActivity,
+ Func, CancellationToken, Task> handler) where TActivity : AgentLifecycleEventActivity
+ {
+ ArgumentNullException.ThrowIfNull(app, nameof(app));
+ ArgumentException.ThrowIfNullOrWhiteSpace(routeSuffix);
+ ArgumentNullException.ThrowIfNull(createActivity, nameof(createActivity));
+ ArgumentNullException.ThrowIfNull(handler, nameof(handler));
+
+ string routeName = valueType is null
+ ? string.Join("/", TeamsActivityTypes.Event, routeSuffix)
+ : string.Join("/", TeamsActivityTypes.Event, EventNames.AgentLifecycle, routeSuffix);
+
+ app.Router.Register(new Route
+ {
+ Name = routeName,
+ Selector = activity =>
+ activity.Name == EventNames.AgentLifecycle
+ && (valueType is null || activity.ValueType == valueType),
+ Handler = async (ctx, cancellationToken) =>
+ {
+ TActivity typedActivity = createActivity(ctx.Activity);
+ var typedContext = ctx.CreateDerivedContext(typedActivity);
+ await handler(typedContext, cancellationToken).ConfigureAwait(false);
+ }
+ });
+
+ return app;
+ }
+}
diff --git a/core/src/Microsoft.Teams.Apps/Handlers/EventHandler.Activity.cs b/core/src/Microsoft.Teams.Apps/Handlers/EventHandler.Activity.cs
index c06830ce..ad792020 100644
--- a/core/src/Microsoft.Teams.Apps/Handlers/EventHandler.Activity.cs
+++ b/core/src/Microsoft.Teams.Apps/Handlers/EventHandler.Activity.cs
@@ -20,7 +20,10 @@ public class EventActivity : TeamsActivity
public static new EventActivity FromActivity(CoreActivity activity)
{
ArgumentNullException.ThrowIfNull(activity);
- return new EventActivity(activity);
+ EventActivity eventActivity = new(activity);
+ return eventActivity.Name == EventNames.AgentLifecycle
+ ? AgentLifecycleEventActivity.FromEventActivity(eventActivity)
+ : eventActivity;
}
///
@@ -29,12 +32,21 @@ public class EventActivity : TeamsActivity
[JsonPropertyName("name")]
public string? Name { get; set; }
+ ///
+ /// Gets or sets the value payload type for event activities that carry typed variants.
+ ///
+ [JsonPropertyName("valueType")]
+ public string? ValueType { get; set; }
+
///
/// Gets or sets the value payload of the event activity.
///
[JsonPropertyName("value")]
public JsonNode? Value { get; set; }
+ ///
+ public override string ToJson() => CoreActivity.ToJson(this);
+
///
/// Initializes a new instance of the class.
///
@@ -48,10 +60,17 @@ internal EventActivity() : base(TeamsActivityTypes.Event)
///
internal EventActivity(CoreActivity activity) : base(activity)
{
- Name = activity.Properties.Extract("name");
- Value = activity is EventActivity evt
- ? evt.Value
- : activity.Properties.Extract("value");
+ if (activity is EventActivity evt)
+ {
+ Name = evt.Name;
+ ValueType = evt.ValueType;
+ Value = evt.Value;
+ return;
+ }
+
+ Name = Properties.Extract("name");
+ ValueType = Properties.Extract("valueType");
+ Value = Properties.Extract("value");
}
}
@@ -91,6 +110,9 @@ internal EventActivity(EventActivity activity) : base(activity)
///
public static class EventNames
{
+ /// Agent 365 lifecycle event name.
+ public const string AgentLifecycle = "agentLifecycle";
+
/// Meeting start event name.
public const string MeetingStart = "application/vnd.microsoft.meetingStart";
diff --git a/core/src/Microsoft.Teams.Apps/Schema/TeamsActivity.cs b/core/src/Microsoft.Teams.Apps/Schema/TeamsActivity.cs
index 54347ad6..4deb08d3 100644
--- a/core/src/Microsoft.Teams.Apps/Schema/TeamsActivity.cs
+++ b/core/src/Microsoft.Teams.Apps/Schema/TeamsActivity.cs
@@ -68,8 +68,8 @@ internal TeamsActivity(CoreActivity activity) : base(activity)
base.From = TeamsChannelAccount.FromChannelAccount(activity.From) ?? new TeamsChannelAccount();
base.Recipient = TeamsChannelAccount.FromChannelAccount(activity.Recipient) ?? new TeamsChannelAccount();
base.Conversation = TeamsConversation.FromConversation(activity.Conversation) ?? new TeamsConversation();
- ChannelData = activity.Properties.Extract("channelData");
- Entities = activity.Properties.Extract("entities");
+ ChannelData = Properties.Extract("channelData");
+ Entities = Properties.Extract("entities");
}
///
diff --git a/core/test/Microsoft.Teams.Apps.UnitTests/AgentLifecycleHandlerTests.cs b/core/test/Microsoft.Teams.Apps.UnitTests/AgentLifecycleHandlerTests.cs
new file mode 100644
index 00000000..3b8dc46d
--- /dev/null
+++ b/core/test/Microsoft.Teams.Apps.UnitTests/AgentLifecycleHandlerTests.cs
@@ -0,0 +1,461 @@
+// Copyright (c) Microsoft Corporation.
+// Licensed under the MIT License.
+
+using System.Text.Json;
+using Microsoft.AspNetCore.Http;
+using Microsoft.Extensions.Configuration;
+using Microsoft.Extensions.Logging.Abstractions;
+using Microsoft.Teams.Apps.Api.Clients;
+using Microsoft.Teams.Apps.Handlers;
+using Microsoft.Teams.Apps.Schema;
+using Microsoft.Teams.Apps.State;
+using Microsoft.Teams.Core;
+using Microsoft.Teams.Core.Schema;
+using Moq;
+
+namespace Microsoft.Teams.Apps.UnitTests;
+
+public class AgentLifecycleHandlerTests
+{
+ private const string TenantId = "00000000-0000-0000-0000-000000000001";
+ private const string AgenticUserId = "00000000-0000-0000-0000-000000000002";
+ private const string AppId = "00000000-0000-0000-0000-000000000003";
+ private const string AgenticAppInstanceId = "00000000-0000-0000-0000-000000000004";
+ private const string BlueprintId = "00000000-0000-0000-0000-000000000005";
+ private const string ManagerId = "3c22b565-74f3-48b0-aa18-1dc03b8ec270";
+
+ public static TheoryData VariantTypes => new()
+ {
+ { AgentLifecycleEventValueTypes.AgenticUserIdentityCreated, typeof(AgenticUserIdentityCreatedActivity) },
+ { AgentLifecycleEventValueTypes.AgenticUserIdentityUpdated, typeof(AgenticUserIdentityUpdatedActivity) },
+ { AgentLifecycleEventValueTypes.AgenticUserManagerUpdated, typeof(AgenticUserManagerUpdatedActivity) },
+ { AgentLifecycleEventValueTypes.AgenticUserEnabled, typeof(AgenticUserEnabledActivity) },
+ { AgentLifecycleEventValueTypes.AgenticUserDisabled, typeof(AgenticUserDisabledActivity) },
+ { AgentLifecycleEventValueTypes.AgenticUserDeleted, typeof(AgenticUserDeletedActivity) },
+ { AgentLifecycleEventValueTypes.AgenticUserUndeleted, typeof(AgenticUserUndeletedActivity) },
+ { AgentLifecycleEventValueTypes.AgenticUserWorkloadOnboardingUpdated, typeof(AgenticUserWorkloadOnboardingUpdatedActivity) },
+ };
+
+ [Theory]
+ [MemberData(nameof(VariantTypes))]
+ public void FromActivity_DiscriminatesAgentLifecycleVariants(string valueType, Type expectedType)
+ {
+ TeamsActivity activity = ParseLifecycleActivity(valueType);
+
+ Assert.Equal(expectedType, activity.GetType());
+
+ EventActivity eventActivity = Assert.IsAssignableFrom(activity);
+ Assert.Equal(TeamsActivityTypes.Event, eventActivity.Type);
+ Assert.Equal(EventNames.AgentLifecycle, eventActivity.Name);
+ Assert.Equal(valueType, eventActivity.ValueType);
+ AssertLifecycleValue(activity, valueType);
+ }
+
+ [Fact]
+ public void FromActivity_KeepsUnknownAgentLifecycleValueTypeAsBaseLifecycleActivity()
+ {
+ TeamsActivity activity = ParseLifecycleActivity("UnknownLifecycleValueType", CommonValue("unknownLifecycleEvent"));
+
+ AgentLifecycleEventActivity lifecycleActivity = Assert.IsType(activity);
+ Assert.Equal(EventNames.AgentLifecycle, lifecycleActivity.Name);
+ Assert.Equal("UnknownLifecycleValueType", lifecycleActivity.ValueType);
+ Assert.NotNull(lifecycleActivity.Value);
+ }
+
+ [Fact]
+ public void ToJson_PreservesAgentLifecycleEnvelope()
+ {
+ AgenticUserManagerUpdatedActivity activity = Assert.IsType(
+ ParseLifecycleActivity(AgentLifecycleEventValueTypes.AgenticUserManagerUpdated));
+
+ string json = activity.ToJson();
+ using JsonDocument document = JsonDocument.Parse(json);
+ JsonElement root = document.RootElement;
+ Assert.Equal(1, CountOccurrences(json, "\"valueType\""));
+ Assert.Equal(1, CountOccurrences(json, "\"valueType\""));
+ Assert.Equal(TeamsActivityTypes.Event, root.GetProperty("type").GetString());
+ Assert.Equal(EventNames.AgentLifecycle, root.GetProperty("name").GetString());
+ Assert.Equal(AgentLifecycleEventValueTypes.AgenticUserManagerUpdated, root.GetProperty("valueType").GetString());
+ Assert.Equal(AgenticAppInstanceId, root.GetProperty("value").GetProperty("agenticAppInstanceId").GetString());
+ Assert.Equal(ManagerId, root.GetProperty("value").GetProperty("manager").GetProperty("managerId").GetString());
+ }
+
+ [Fact]
+ public async Task OnEvent_StillReceivesAgentLifecycleEvents()
+ {
+ TeamsBotApplication app = CreateApp();
+ bool called = false;
+
+ app.OnEvent((ctx, _) =>
+ {
+ called = true;
+ Assert.IsType(ctx.Activity);
+ Assert.Equal(EventNames.AgentLifecycle, ctx.Activity.Name);
+ Assert.Equal(AgentLifecycleEventValueTypes.AgenticUserEnabled, ctx.Activity.ValueType);
+ return Task.CompletedTask;
+ });
+
+ await DispatchAsync(app, AgentLifecycleEventValueTypes.AgenticUserEnabled);
+
+ Assert.True(called);
+ }
+
+ [Theory]
+ [MemberData(nameof(VariantTypes))]
+ public async Task VariantHandlers_DispatchTypedContextAndPropagateState(string valueType, Type expectedType)
+ {
+ TeamsBotApplication app = CreateApp();
+ TurnStateContainer state = CreateState();
+ int lifecycleHandlerCalls = 0;
+ int variantHandlerCalls = 0;
+
+ app.OnAgentLifecycle((ctx, _) =>
+ {
+ lifecycleHandlerCalls++;
+ Assert.Same(state, ctx.State);
+ Assert.Equal(valueType, ctx.Activity.ValueType);
+ Assert.Equal(expectedType, AgentLifecycleEventActivity.FromEventActivity(ctx.Activity).GetType());
+ return Task.CompletedTask;
+ });
+
+ RegisterVariantHandler(app, valueType, expectedType, state, () => variantHandlerCalls++);
+
+ await DispatchAsync(app, valueType, state);
+
+ Assert.Equal(1, lifecycleHandlerCalls);
+ Assert.Equal(1, variantHandlerCalls);
+ }
+
+ [Fact]
+ public async Task VariantHandler_DoesNotDispatchForDifferentValueType()
+ {
+ TeamsBotApplication app = CreateApp();
+ bool called = false;
+
+ app.OnAgenticUserEnabled((_, _) =>
+ {
+ called = true;
+ return Task.CompletedTask;
+ });
+
+ await DispatchAsync(app, AgentLifecycleEventValueTypes.AgenticUserDisabled);
+
+ Assert.False(called);
+ }
+
+ private static void RegisterVariantHandler(
+ TeamsBotApplication app,
+ string valueType,
+ Type expectedType,
+ TurnStateContainer state,
+ Action onCalled)
+ {
+ switch (valueType)
+ {
+ case AgentLifecycleEventValueTypes.AgenticUserIdentityCreated:
+ app.OnAgenticUserIdentityCreated((ctx, _) =>
+ {
+ AssertVariantContext(ctx, expectedType, valueType, state, onCalled);
+ Assert.Equal(ManagerId, ctx.Activity.Value?.Manager?.UserId);
+ return Task.CompletedTask;
+ });
+ break;
+
+ case AgentLifecycleEventValueTypes.AgenticUserIdentityUpdated:
+ app.OnAgenticUserIdentityUpdated((ctx, _) =>
+ {
+ AssertVariantContext(ctx, expectedType, valueType, state, onCalled);
+ Assert.Equal("Mail", ctx.Activity.Value?.UpdatedProperty.PropertyName);
+ return Task.CompletedTask;
+ });
+ break;
+
+ case AgentLifecycleEventValueTypes.AgenticUserManagerUpdated:
+ app.OnAgenticUserManagerUpdated((ctx, _) =>
+ {
+ AssertVariantContext(ctx, expectedType, valueType, state, onCalled);
+ Assert.Equal(ManagerId, ctx.Activity.Value?.Manager?.ManagerId);
+ return Task.CompletedTask;
+ });
+ break;
+
+ case AgentLifecycleEventValueTypes.AgenticUserEnabled:
+ app.OnAgenticUserEnabled((ctx, _) =>
+ {
+ AssertVariantContext(ctx, expectedType, valueType, state, onCalled);
+ Assert.Equal(6, ctx.Activity.Value?.Version);
+ return Task.CompletedTask;
+ });
+ break;
+
+ case AgentLifecycleEventValueTypes.AgenticUserDisabled:
+ app.OnAgenticUserDisabled((ctx, _) =>
+ {
+ AssertVariantContext(ctx, expectedType, valueType, state, onCalled);
+ Assert.Equal(7, ctx.Activity.Value?.Version);
+ return Task.CompletedTask;
+ });
+ break;
+
+ case AgentLifecycleEventValueTypes.AgenticUserDeleted:
+ app.OnAgenticUserDeleted((ctx, _) =>
+ {
+ AssertVariantContext(ctx, expectedType, valueType, state, onCalled);
+ Assert.Equal("UserSoftDelete", ctx.Activity.Value?.DeletionReason);
+ return Task.CompletedTask;
+ });
+ break;
+
+ case AgentLifecycleEventValueTypes.AgenticUserUndeleted:
+ app.OnAgenticUserUndeleted((ctx, _) =>
+ {
+ AssertVariantContext(ctx, expectedType, valueType, state, onCalled);
+ Assert.Equal(9, ctx.Activity.Value?.Version);
+ return Task.CompletedTask;
+ });
+ break;
+
+ case AgentLifecycleEventValueTypes.AgenticUserWorkloadOnboardingUpdated:
+ app.OnAgenticUserWorkloadOnboardingUpdated((ctx, _) =>
+ {
+ AssertVariantContext(ctx, expectedType, valueType, state, onCalled);
+ Assert.Equal("Teams", ctx.Activity.Value?.WorkloadName);
+ Assert.Equal("succeeded", ctx.Activity.Value?.WorkloadOnboardingState);
+ return Task.CompletedTask;
+ });
+ break;
+ }
+ }
+
+ private static void AssertVariantContext(
+ Context context,
+ Type expectedType,
+ string valueType,
+ TurnStateContainer state,
+ Action onCalled) where TActivity : AgentLifecycleEventActivity
+ {
+ onCalled();
+ Assert.Same(state, context.State);
+ Assert.Equal(expectedType, context.Activity.GetType());
+ Assert.Equal(EventNames.AgentLifecycle, context.Activity.Name);
+ Assert.Equal(valueType, context.Activity.ValueType);
+ }
+
+ private static void AssertLifecycleValue(TeamsActivity activity, string valueType)
+ {
+ switch (valueType)
+ {
+ case AgentLifecycleEventValueTypes.AgenticUserIdentityCreated:
+ AgenticUserIdentityCreatedActivity created = Assert.IsType(activity);
+ Assert.Equal(AgentLifecycleEventTypes.AgenticUserIdentityCreated, created.Value?.EventType);
+ Assert.Equal(AgenticUserId, created.Value?.AgenticUserId);
+ Assert.Equal(ManagerId, created.Value?.Manager?.UserId);
+ Assert.Equal("manager@example.test", created.Value?.Manager?.Email);
+ Assert.Equal(1, created.Value?.ExpirationDateTime?.Year);
+ break;
+
+ case AgentLifecycleEventValueTypes.AgenticUserIdentityUpdated:
+ AgenticUserIdentityUpdatedActivity updated = Assert.IsType(activity);
+ Assert.Equal("Mail", updated.Value?.UpdatedProperty.PropertyName);
+ Assert.Equal("newinstance4@teamssdk.onmicrosoft.com", updated.Value?.UpdatedProperty.PropertyValue);
+ Assert.Equal(4, updated.Value?.Version);
+ break;
+
+ case AgentLifecycleEventValueTypes.AgenticUserManagerUpdated:
+ AgenticUserManagerUpdatedActivity managerUpdated = Assert.IsType(activity);
+ Assert.Equal(ManagerId, managerUpdated.Value?.Manager?.ManagerId);
+ Assert.Equal(6, managerUpdated.Value?.Version);
+ break;
+
+ case AgentLifecycleEventValueTypes.AgenticUserEnabled:
+ AgenticUserEnabledActivity enabled = Assert.IsType(activity);
+ Assert.Equal(AgentLifecycleEventTypes.AgenticUserEnabled, enabled.Value?.EventType);
+ Assert.Equal(6, enabled.Value?.Version);
+ break;
+
+ case AgentLifecycleEventValueTypes.AgenticUserDisabled:
+ AgenticUserDisabledActivity disabled = Assert.IsType(activity);
+ Assert.Equal(AgentLifecycleEventTypes.AgenticUserDisabled, disabled.Value?.EventType);
+ Assert.Equal(7, disabled.Value?.Version);
+ break;
+
+ case AgentLifecycleEventValueTypes.AgenticUserDeleted:
+ AgenticUserDeletedActivity deleted = Assert.IsType(activity);
+ Assert.Equal("UserSoftDelete", deleted.Value?.DeletionReason);
+ Assert.Equal(8, deleted.Value?.Version);
+ break;
+
+ case AgentLifecycleEventValueTypes.AgenticUserUndeleted:
+ AgenticUserUndeletedActivity undeleted = Assert.IsType(activity);
+ Assert.Equal(AgentLifecycleEventTypes.AgenticUserUndeleted, undeleted.Value?.EventType);
+ Assert.Equal(9, undeleted.Value?.Version);
+ break;
+
+ case AgentLifecycleEventValueTypes.AgenticUserWorkloadOnboardingUpdated:
+ AgenticUserWorkloadOnboardingUpdatedActivity workload = Assert.IsType(activity);
+ Assert.Equal("Teams", workload.Value?.WorkloadName);
+ Assert.Equal("succeeded", workload.Value?.WorkloadOnboardingState);
+ break;
+ }
+ }
+
+ private static Task DispatchAsync(TeamsBotApplication app, string valueType, TurnStateContainer? state = null)
+ {
+ EventActivity activity = Assert.IsAssignableFrom(ParseLifecycleActivity(valueType));
+ Context context = new(app, activity);
+ if (state is not null)
+ {
+ context.State = state;
+ }
+
+ return app.Router.DispatchAsync(context);
+ }
+
+ private static TeamsActivity ParseLifecycleActivity(string valueType)
+ => ParseLifecycleActivity(valueType, ValueFor(valueType));
+
+ private static TeamsActivity ParseLifecycleActivity(string valueType, string valueJson)
+ {
+ string json = $$"""
+ {
+ "recipient": {
+ "agenticUserId": "{{AgenticUserId}}",
+ "agenticAppId": "{{AppId}}",
+ "agenticAppBlueprintId": "{{BlueprintId}}",
+ "callbackUri": "https://example.test/api/messages",
+ "tenantId": "{{TenantId}}",
+ "role": "agenticUser",
+ "id": "{{AgenticUserId}}"
+ },
+ "type": "event",
+ "id": "activity-id",
+ "timestamp": "2026-06-29T00:00:00Z",
+ "serviceUrl": "https://smba.trafficmanager.net/amer/tenant/",
+ "channelId": "agents",
+ "from": { "id": "system", "name": "System", "tenantId": "{{TenantId}}" },
+ "conversation": { "tenantId": "{{TenantId}}", "id": "conversation-id", "topic": null },
+ "channelData": { "tenant": { "id": "{{TenantId}}" }, "productContext": null },
+ "valueType": "{{valueType}}",
+ "value": {{valueJson}},
+ "name": "agentLifecycle"
+ }
+ """;
+
+ return TeamsActivity.FromActivity(CoreActivity.FromJsonString(json));
+ }
+
+ private static string ValueFor(string valueType)
+ => valueType switch
+ {
+ AgentLifecycleEventValueTypes.AgenticUserIdentityCreated => CommonValue(
+ AgentLifecycleEventTypes.AgenticUserIdentityCreated,
+ $$"""
+ "expirationDateTime": "0001-01-01T00:00:00+00:00",
+ "manager": {
+ "displayName": null,
+ "userId": "{{ManagerId}}",
+ "email": "manager@example.test"
+ }
+ """),
+ AgentLifecycleEventValueTypes.AgenticUserIdentityUpdated => CommonValue(
+ AgentLifecycleEventTypes.AgenticUserIdentityUpdated,
+ """
+ "updatedProperty": {
+ "propertyName": "Mail",
+ "propertyValue": "newinstance4@teamssdk.onmicrosoft.com"
+ },
+ "version": 4
+ """),
+ AgentLifecycleEventValueTypes.AgenticUserManagerUpdated => CommonValue(
+ AgentLifecycleEventTypes.AgenticUserManagerUpdated,
+ $$"""
+ "manager": { "managerId": "{{ManagerId}}" },
+ "version": 6
+ """),
+ AgentLifecycleEventValueTypes.AgenticUserEnabled => CommonValue(
+ AgentLifecycleEventTypes.AgenticUserEnabled,
+ """
+ "version": 6
+ """),
+ AgentLifecycleEventValueTypes.AgenticUserDisabled => CommonValue(
+ AgentLifecycleEventTypes.AgenticUserDisabled,
+ """
+ "version": 7
+ """),
+ AgentLifecycleEventValueTypes.AgenticUserDeleted => CommonValue(
+ AgentLifecycleEventTypes.AgenticUserDeleted,
+ """
+ "deletionReason": "UserSoftDelete",
+ "version": 8
+ """),
+ AgentLifecycleEventValueTypes.AgenticUserUndeleted => CommonValue(
+ AgentLifecycleEventTypes.AgenticUserUndeleted,
+ """
+ "version": 9
+ """),
+ AgentLifecycleEventValueTypes.AgenticUserWorkloadOnboardingUpdated => CommonValue(
+ AgentLifecycleEventTypes.AgenticUserWorkloadOnboardingUpdated,
+ """
+ "workloadName": "Teams",
+ "workloadOnboardingState": "succeeded"
+ """),
+ _ => CommonValue("unknownLifecycleEvent"),
+ };
+
+ private static string CommonValue(string eventType, string additionalJson = "")
+ {
+ string separator = string.IsNullOrWhiteSpace(additionalJson) ? string.Empty : "," + Environment.NewLine + additionalJson;
+ return $$"""
+ {
+ "tenantId": "{{TenantId}}",
+ "agenticUserId": "{{AgenticUserId}}",
+ "agenticAppInstanceId": "{{AgenticAppInstanceId}}",
+ "agentIdentityBlueprintId": "{{BlueprintId}}",
+ "eventType": "{{eventType}}"{{separator}}
+ }
+ """;
+ }
+
+ private static int CountOccurrences(string value, string match)
+ {
+ int count = 0;
+ int index = 0;
+ while ((index = value.IndexOf(match, index, StringComparison.Ordinal)) >= 0)
+ {
+ count++;
+ index += match.Length;
+ }
+
+ return count;
+ }
+
+ private static TurnStateContainer CreateState()
+ {
+ TurnState conversationState = new();
+ conversationState.Set("test-key", "test-value");
+ return new TurnStateContainer(conversationState, new TurnState());
+ }
+
+ private static TeamsBotApplication CreateApp()
+ {
+ Mock mockUserTokenClient = new(
+ new HttpClient(),
+ new Mock().Object,
+ NullLogger.Instance);
+
+ Mock mockConversationClient = new(
+ new HttpClient(),
+ NullLogger.Instance);
+
+ ApiClient apiClient = new(
+ new HttpClient(),
+ mockConversationClient.Object,
+ mockUserTokenClient.Object);
+
+ return new TeamsBotApplication(
+ apiClient,
+ new HttpContextAccessor(),
+ NullLogger.Instance,
+ new TeamsBotApplicationOptions { AppId = "test-app-id" });
+ }
+}