diff --git a/frontend/interfaces/activity.ts b/frontend/interfaces/activity.ts
index b2c6b55d31..bee2119427 100644
--- a/frontend/interfaces/activity.ts
+++ b/frontend/interfaces/activity.ts
@@ -228,7 +228,15 @@ export type IHostPastActivityType =
| ActivityType.RotatedManagedLocalAccountPassword
| ActivityType.FailedToRotateManagedLocalAccountPassword
| ActivityType.FailedEnrollmentProfileRenewal
- | ActivityType.RanCustomMdmCommand;
+ | ActivityType.RanCustomMdmCommand
+ | ActivityType.RanAutomationWebhook
+ | ActivityType.RanAutomationTicket
+ | ActivityType.RanAutomationCalendarEvent
+ | ActivityType.RanAutomationConditionalAccess
+ | ActivityType.FailedAutomationWebhook
+ | ActivityType.FailedAutomationTicket
+ | ActivityType.FailedAutomationCalendarEvent
+ | ActivityType.FailedAutomationConditionalAccess;
/** This is a subset of ActivityType that are shown only for the host upcoming activities */
export type IHostUpcomingActivityType =
diff --git a/frontend/pages/hosts/details/cards/Activity/ActivityConfig.tsx b/frontend/pages/hosts/details/cards/Activity/ActivityConfig.tsx
index d8464fcb98..54ed94e69c 100644
--- a/frontend/pages/hosts/details/cards/Activity/ActivityConfig.tsx
+++ b/frontend/pages/hosts/details/cards/Activity/ActivityConfig.tsx
@@ -36,6 +36,7 @@ import FailedToRotateManagedLocalAccountPasswordActivityItem from "./ActivityIte
import FailedEnrollmentProfileRenewalActivityItem from "./ActivityItems/FailedEnrollmentProfileRenewalActivityItem";
import MdmUnenrolledActivityItem from "./ActivityItems/MdmUnenrolledActivityItem";
import RanCustomMdmCommandActivityItem from "./ActivityItems/RanCustomMdmCommandActivityItem";
+import PolicyAutomationActivityItem from "./ActivityItems/PolicyAutomationActivityItem";
/** The component props that all host activity items must adhere to */
export interface IHostActivityItemComponentProps {
@@ -92,6 +93,14 @@ export const pastActivityComponentMap: Record<
[ActivityType.FailedEnrollmentProfileRenewal]: FailedEnrollmentProfileRenewalActivityItem,
[ActivityType.MdmUnenrolled]: MdmUnenrolledActivityItem,
[ActivityType.RanCustomMdmCommand]: RanCustomMdmCommandActivityItem,
+ [ActivityType.RanAutomationWebhook]: PolicyAutomationActivityItem,
+ [ActivityType.RanAutomationTicket]: PolicyAutomationActivityItem,
+ [ActivityType.RanAutomationCalendarEvent]: PolicyAutomationActivityItem,
+ [ActivityType.RanAutomationConditionalAccess]: PolicyAutomationActivityItem,
+ [ActivityType.FailedAutomationWebhook]: PolicyAutomationActivityItem,
+ [ActivityType.FailedAutomationTicket]: PolicyAutomationActivityItem,
+ [ActivityType.FailedAutomationCalendarEvent]: PolicyAutomationActivityItem,
+ [ActivityType.FailedAutomationConditionalAccess]: PolicyAutomationActivityItem,
};
export const upcomingActivityComponentMap: Record<
diff --git a/frontend/pages/hosts/details/cards/Activity/ActivityItems/PolicyAutomationActivityItem/PolicyAutomationActivityItem.tests.tsx b/frontend/pages/hosts/details/cards/Activity/ActivityItems/PolicyAutomationActivityItem/PolicyAutomationActivityItem.tests.tsx
new file mode 100644
index 0000000000..af9ec1395e
--- /dev/null
+++ b/frontend/pages/hosts/details/cards/Activity/ActivityItems/PolicyAutomationActivityItem/PolicyAutomationActivityItem.tests.tsx
@@ -0,0 +1,83 @@
+import React from "react";
+import { render, screen } from "@testing-library/react";
+import { createMockHostPastActivity } from "__mocks__/activityMock";
+
+import { ActivityType, IHostPastActivityType } from "interfaces/activity";
+
+import PolicyAutomationActivityItem from "./PolicyAutomationActivityItem";
+
+const renderItem = (type: IHostPastActivityType) =>
+ render(
+
+ );
+
+describe("PolicyAutomationActivityItem", () => {
+ const cases: Array<[IHostPastActivityType, RegExp]> = [
+ [ActivityType.RanAutomationWebhook, /sent a webhook because this host/i],
+ [ActivityType.RanAutomationTicket, /created a ticket because this host/i],
+ [
+ ActivityType.RanAutomationCalendarEvent,
+ /created a calendar event because this host/i,
+ ],
+ [
+ ActivityType.RanAutomationConditionalAccess,
+ /blocked single sign-on because this host/i,
+ ],
+ [
+ ActivityType.FailedAutomationWebhook,
+ /failed to send a webhook after this host/i,
+ ],
+ [
+ ActivityType.FailedAutomationTicket,
+ /failed to create a ticket after this host/i,
+ ],
+ [
+ ActivityType.FailedAutomationCalendarEvent,
+ /failed to create a calendar event after this host/i,
+ ],
+ [
+ ActivityType.FailedAutomationConditionalAccess,
+ /failed to block single sign-on after this host/i,
+ ],
+ ];
+
+ it.each(cases)("renders copy for %s", (type, expected) => {
+ renderItem(type);
+ expect(screen.getByText("Fleet")).toBeVisible();
+ expect(screen.getByText(expected)).toBeVisible();
+ });
+
+ it("does not render the cancel or show-details icons", () => {
+ renderItem(ActivityType.RanAutomationWebhook);
+ expect(screen.queryByTestId("close-icon")).not.toBeInTheDocument();
+ expect(screen.queryByTestId("info-outline-icon")).not.toBeInTheDocument();
+ });
+
+ // These activities are always Fleet-initiated in practice; this documents the
+ // defensive actor branch that keeps the bold name in sync with the avatar.
+ it("renders the actor name when the activity is not Fleet-initiated", () => {
+ render(
+
+ );
+ expect(screen.getByText("Admin User")).toBeVisible();
+ expect(screen.queryByText("Fleet")).not.toBeInTheDocument();
+ });
+});
diff --git a/frontend/pages/hosts/details/cards/Activity/ActivityItems/PolicyAutomationActivityItem/PolicyAutomationActivityItem.tsx b/frontend/pages/hosts/details/cards/Activity/ActivityItems/PolicyAutomationActivityItem/PolicyAutomationActivityItem.tsx
new file mode 100644
index 0000000000..3a7d1bc8c9
--- /dev/null
+++ b/frontend/pages/hosts/details/cards/Activity/ActivityItems/PolicyAutomationActivityItem/PolicyAutomationActivityItem.tsx
@@ -0,0 +1,65 @@
+import React from "react";
+
+import { ActivityType, IHostPastActivityType } from "interfaces/activity";
+import ActivityItem from "components/ActivityItem";
+
+import { IHostActivityItemComponentProps } from "../../ActivityConfig";
+
+const baseClass = "policy-automation-activity-item";
+
+type PolicyAutomationActivityType =
+ | ActivityType.RanAutomationWebhook
+ | ActivityType.RanAutomationTicket
+ | ActivityType.RanAutomationCalendarEvent
+ | ActivityType.RanAutomationConditionalAccess
+ | ActivityType.FailedAutomationWebhook
+ | ActivityType.FailedAutomationTicket
+ | ActivityType.FailedAutomationCalendarEvent
+ | ActivityType.FailedAutomationConditionalAccess;
+
+const AUTOMATION_COPY: Record = {
+ [ActivityType.RanAutomationWebhook]:
+ "sent a webhook because this host failed a policy.",
+ [ActivityType.RanAutomationTicket]:
+ "created a ticket because this host failed a policy.",
+ [ActivityType.RanAutomationCalendarEvent]:
+ "created a calendar event because this host failed a policy.",
+ [ActivityType.RanAutomationConditionalAccess]:
+ "blocked single sign-on because this host failed a policy.",
+ [ActivityType.FailedAutomationWebhook]:
+ "failed to send a webhook after this host failed a policy.",
+ [ActivityType.FailedAutomationTicket]:
+ "failed to create a ticket after this host failed a policy.",
+ [ActivityType.FailedAutomationCalendarEvent]:
+ "failed to create a calendar event after this host failed a policy.",
+ [ActivityType.FailedAutomationConditionalAccess]:
+ "failed to block single sign-on after this host failed a policy.",
+};
+
+const isPolicyAutomationActivityType = (
+ type: IHostPastActivityType
+): type is PolicyAutomationActivityType => type in AUTOMATION_COPY;
+
+const PolicyAutomationActivityItem = ({
+ activity,
+ isSoloActivity,
+}: IHostActivityItemComponentProps) => {
+ if (!isPolicyAutomationActivityType(activity.type)) {
+ return null;
+ }
+
+ return (
+
+ {activity.fleet_initiated ? "Fleet" : activity.actor_full_name}{" "}
+ {AUTOMATION_COPY[activity.type]}
+
+ );
+};
+
+export default PolicyAutomationActivityItem;
diff --git a/frontend/pages/hosts/details/cards/Activity/ActivityItems/PolicyAutomationActivityItem/index.ts b/frontend/pages/hosts/details/cards/Activity/ActivityItems/PolicyAutomationActivityItem/index.ts
new file mode 100644
index 0000000000..29bbb35589
--- /dev/null
+++ b/frontend/pages/hosts/details/cards/Activity/ActivityItems/PolicyAutomationActivityItem/index.ts
@@ -0,0 +1 @@
+export { default } from "./PolicyAutomationActivityItem";