From 7017b90bf726d9dcca1f87d16bda96cc8db760ec Mon Sep 17 00:00:00 2001 From: Eden Zimbelman Date: Fri, 21 Aug 2026 17:21:00 -0700 Subject: [PATCH] feat: add agent_session_stopped and agent_session_title_changed events Adds event models, payload wrappers, and handler base classes for the two agent session events an agent app receives, mirroring the assistant event pattern. Payload fields captured from live events. - agent_session_stopped: type, channel, user, thread_ts, streaming_message_ts (array), event_ts - agent_session_title_changed: type, channel, user, team_id, thread_ts, title, previous_title, event_ts Co-Authored-By: Claude --- .../model/event/AgentSessionStoppedEvent.java | 23 +++++++++ .../event/AgentSessionTitleChangedEvent.java | 23 +++++++++ .../event/AgentSessionStoppedEventTest.java | 46 +++++++++++++++++ .../AgentSessionTitleChangedEventTest.java | 49 +++++++++++++++++++ .../handler/AgentSessionStoppedHandler.java | 13 +++++ .../AgentSessionTitleChangedHandler.java | 13 +++++ .../payload/AgentSessionStoppedPayload.java | 25 ++++++++++ .../AgentSessionTitleChangedPayload.java | 25 ++++++++++ 8 files changed, 217 insertions(+) create mode 100644 slack-api-model/src/main/java/com/slack/api/model/event/AgentSessionStoppedEvent.java create mode 100644 slack-api-model/src/main/java/com/slack/api/model/event/AgentSessionTitleChangedEvent.java create mode 100644 slack-api-model/src/test/java/test_locally/api/model/event/AgentSessionStoppedEventTest.java create mode 100644 slack-api-model/src/test/java/test_locally/api/model/event/AgentSessionTitleChangedEventTest.java create mode 100644 slack-app-backend/src/main/java/com/slack/api/app_backend/events/handler/AgentSessionStoppedHandler.java create mode 100644 slack-app-backend/src/main/java/com/slack/api/app_backend/events/handler/AgentSessionTitleChangedHandler.java create mode 100644 slack-app-backend/src/main/java/com/slack/api/app_backend/events/payload/AgentSessionStoppedPayload.java create mode 100644 slack-app-backend/src/main/java/com/slack/api/app_backend/events/payload/AgentSessionTitleChangedPayload.java diff --git a/slack-api-model/src/main/java/com/slack/api/model/event/AgentSessionStoppedEvent.java b/slack-api-model/src/main/java/com/slack/api/model/event/AgentSessionStoppedEvent.java new file mode 100644 index 000000000..5d5b313b1 --- /dev/null +++ b/slack-api-model/src/main/java/com/slack/api/model/event/AgentSessionStoppedEvent.java @@ -0,0 +1,23 @@ +package com.slack.api.model.event; + +import lombok.Data; + +import java.util.List; + +/** + * The user clicked the native stop button while the agent session was processing. + *

+ * https://docs.slack.dev/reference/events/agent_session_stopped + */ +@Data +public class AgentSessionStoppedEvent implements Event { + + public static final String TYPE_NAME = "agent_session_stopped"; + + private final String type = TYPE_NAME; + private String channel; + private String user; + private String threadTs; + private List streamingMessageTs; + private String eventTs; +} diff --git a/slack-api-model/src/main/java/com/slack/api/model/event/AgentSessionTitleChangedEvent.java b/slack-api-model/src/main/java/com/slack/api/model/event/AgentSessionTitleChangedEvent.java new file mode 100644 index 000000000..7bc32b2d8 --- /dev/null +++ b/slack-api-model/src/main/java/com/slack/api/model/event/AgentSessionTitleChangedEvent.java @@ -0,0 +1,23 @@ +package com.slack.api.model.event; + +import lombok.Data; + +/** + * The user renamed an agent session. + *

+ * https://docs.slack.dev/reference/events/agent_session_title_changed + */ +@Data +public class AgentSessionTitleChangedEvent implements Event { + + public static final String TYPE_NAME = "agent_session_title_changed"; + + private final String type = TYPE_NAME; + private String channel; + private String user; + private String teamId; + private String threadTs; + private String title; + private String previousTitle; + private String eventTs; +} diff --git a/slack-api-model/src/test/java/test_locally/api/model/event/AgentSessionStoppedEventTest.java b/slack-api-model/src/test/java/test_locally/api/model/event/AgentSessionStoppedEventTest.java new file mode 100644 index 000000000..6dfed9f9a --- /dev/null +++ b/slack-api-model/src/test/java/test_locally/api/model/event/AgentSessionStoppedEventTest.java @@ -0,0 +1,46 @@ +package test_locally.api.model.event; + +import com.google.gson.Gson; +import com.slack.api.model.event.AgentSessionStoppedEvent; +import org.junit.Test; +import test_locally.unit.GsonFactory; + +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.MatcherAssert.assertThat; + +public class AgentSessionStoppedEventTest { + + @Test + public void typeName() { + assertThat(AgentSessionStoppedEvent.TYPE_NAME, is("agent_session_stopped")); + } + + @Test + public void deserialize() { + String json = "{\n" + + " \"type\": \"agent_session_stopped\",\n" + + " \"channel\": \"C0123ABC456\",\n" + + " \"user\": \"U0123ABC456\",\n" + + " \"streaming_message_ts\": [\"1787267140.336949\"],\n" + + " \"thread_ts\": \"1787267126.742989\",\n" + + " \"event_ts\": \"1787267141.948630\"\n" + + "}"; + AgentSessionStoppedEvent event = GsonFactory.createSnakeCase().fromJson(json, AgentSessionStoppedEvent.class); + assertThat(event.getType(), is("agent_session_stopped")); + assertThat(event.getChannel(), is("C0123ABC456")); + assertThat(event.getUser(), is("U0123ABC456")); + assertThat(event.getStreamingMessageTs().size(), is(1)); + assertThat(event.getStreamingMessageTs().get(0), is("1787267140.336949")); + assertThat(event.getThreadTs(), is("1787267126.742989")); + assertThat(event.getEventTs(), is("1787267141.948630")); + } + + @Test + public void serialize() { + Gson gson = GsonFactory.createSnakeCase(); + AgentSessionStoppedEvent event = new AgentSessionStoppedEvent(); + String generatedJson = gson.toJson(event); + String expectedJson = "{\"type\":\"agent_session_stopped\"}"; + assertThat(generatedJson, is(expectedJson)); + } +} diff --git a/slack-api-model/src/test/java/test_locally/api/model/event/AgentSessionTitleChangedEventTest.java b/slack-api-model/src/test/java/test_locally/api/model/event/AgentSessionTitleChangedEventTest.java new file mode 100644 index 000000000..12877d05b --- /dev/null +++ b/slack-api-model/src/test/java/test_locally/api/model/event/AgentSessionTitleChangedEventTest.java @@ -0,0 +1,49 @@ +package test_locally.api.model.event; + +import com.google.gson.Gson; +import com.slack.api.model.event.AgentSessionTitleChangedEvent; +import org.junit.Test; +import test_locally.unit.GsonFactory; + +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.MatcherAssert.assertThat; + +public class AgentSessionTitleChangedEventTest { + + @Test + public void typeName() { + assertThat(AgentSessionTitleChangedEvent.TYPE_NAME, is("agent_session_title_changed")); + } + + @Test + public void deserialize() { + String json = "{\n" + + " \"type\": \"agent_session_title_changed\",\n" + + " \"channel\": \"C0123ABC456\",\n" + + " \"user\": \"U0123ABC456\",\n" + + " \"team_id\": \"T0123ABC456\",\n" + + " \"thread_ts\": \"1787267269.063879\",\n" + + " \"title\": \"New title\",\n" + + " \"previous_title\": \"Old title\",\n" + + " \"event_ts\": \"1787267281.002810\"\n" + + "}"; + AgentSessionTitleChangedEvent event = GsonFactory.createSnakeCase().fromJson(json, AgentSessionTitleChangedEvent.class); + assertThat(event.getType(), is("agent_session_title_changed")); + assertThat(event.getChannel(), is("C0123ABC456")); + assertThat(event.getUser(), is("U0123ABC456")); + assertThat(event.getTeamId(), is("T0123ABC456")); + assertThat(event.getThreadTs(), is("1787267269.063879")); + assertThat(event.getTitle(), is("New title")); + assertThat(event.getPreviousTitle(), is("Old title")); + assertThat(event.getEventTs(), is("1787267281.002810")); + } + + @Test + public void serialize() { + Gson gson = GsonFactory.createSnakeCase(); + AgentSessionTitleChangedEvent event = new AgentSessionTitleChangedEvent(); + String generatedJson = gson.toJson(event); + String expectedJson = "{\"type\":\"agent_session_title_changed\"}"; + assertThat(generatedJson, is(expectedJson)); + } +} diff --git a/slack-app-backend/src/main/java/com/slack/api/app_backend/events/handler/AgentSessionStoppedHandler.java b/slack-app-backend/src/main/java/com/slack/api/app_backend/events/handler/AgentSessionStoppedHandler.java new file mode 100644 index 000000000..745b8a1fa --- /dev/null +++ b/slack-app-backend/src/main/java/com/slack/api/app_backend/events/handler/AgentSessionStoppedHandler.java @@ -0,0 +1,13 @@ +package com.slack.api.app_backend.events.handler; + +import com.slack.api.app_backend.events.EventHandler; +import com.slack.api.app_backend.events.payload.AgentSessionStoppedPayload; +import com.slack.api.model.event.AgentSessionStoppedEvent; + +public abstract class AgentSessionStoppedHandler extends EventHandler { + + @Override + public String getEventType() { + return AgentSessionStoppedEvent.TYPE_NAME; + } +} diff --git a/slack-app-backend/src/main/java/com/slack/api/app_backend/events/handler/AgentSessionTitleChangedHandler.java b/slack-app-backend/src/main/java/com/slack/api/app_backend/events/handler/AgentSessionTitleChangedHandler.java new file mode 100644 index 000000000..84e204d4a --- /dev/null +++ b/slack-app-backend/src/main/java/com/slack/api/app_backend/events/handler/AgentSessionTitleChangedHandler.java @@ -0,0 +1,13 @@ +package com.slack.api.app_backend.events.handler; + +import com.slack.api.app_backend.events.EventHandler; +import com.slack.api.app_backend.events.payload.AgentSessionTitleChangedPayload; +import com.slack.api.model.event.AgentSessionTitleChangedEvent; + +public abstract class AgentSessionTitleChangedHandler extends EventHandler { + + @Override + public String getEventType() { + return AgentSessionTitleChangedEvent.TYPE_NAME; + } +} diff --git a/slack-app-backend/src/main/java/com/slack/api/app_backend/events/payload/AgentSessionStoppedPayload.java b/slack-app-backend/src/main/java/com/slack/api/app_backend/events/payload/AgentSessionStoppedPayload.java new file mode 100644 index 000000000..dbdd252db --- /dev/null +++ b/slack-app-backend/src/main/java/com/slack/api/app_backend/events/payload/AgentSessionStoppedPayload.java @@ -0,0 +1,25 @@ +package com.slack.api.app_backend.events.payload; + +import com.slack.api.model.event.AgentSessionStoppedEvent; +import lombok.Data; + +import java.util.List; + +@Data +public class AgentSessionStoppedPayload implements EventsApiPayload { + + private String token; + private String enterpriseId; + private String teamId; + private String apiAppId; + private String type; + private List authedUsers; + private List authedTeams; + private List authorizations; + private boolean isExtSharedChannel; + private String eventId; + private Integer eventTime; + private String eventContext; + + private AgentSessionStoppedEvent event; +} diff --git a/slack-app-backend/src/main/java/com/slack/api/app_backend/events/payload/AgentSessionTitleChangedPayload.java b/slack-app-backend/src/main/java/com/slack/api/app_backend/events/payload/AgentSessionTitleChangedPayload.java new file mode 100644 index 000000000..36618d65e --- /dev/null +++ b/slack-app-backend/src/main/java/com/slack/api/app_backend/events/payload/AgentSessionTitleChangedPayload.java @@ -0,0 +1,25 @@ +package com.slack.api.app_backend.events.payload; + +import com.slack.api.model.event.AgentSessionTitleChangedEvent; +import lombok.Data; + +import java.util.List; + +@Data +public class AgentSessionTitleChangedPayload implements EventsApiPayload { + + private String token; + private String enterpriseId; + private String teamId; + private String apiAppId; + private String type; + private List authedUsers; + private List authedTeams; + private List authorizations; + private boolean isExtSharedChannel; + private String eventId; + private Integer eventTime; + private String eventContext; + + private AgentSessionTitleChangedEvent event; +}