From 2b7b7f8dea28909084d21a172b93b466df96984a Mon Sep 17 00:00:00 2001 From: Rajan Date: Tue, 31 Mar 2026 18:34:59 -0400 Subject: [PATCH 1/3] fix: remove unsendable activity types from ActivityParams union (#338) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The ActivityParams union included 10 *ActivityInput types, but the Teams service (APX) only accepts `message` and `typing` on outbound POST/PUT. The other types (messageDelete, messageUpdate, conversationUpdate, handoff, trace, commandSend, commandResult) are inbound-only event notifications — sending them produces gateway errors. Narrowing the union to MessageActivityInput, TypingActivityInput, and MessageReactionActivityInput (kept temporarily — needs its own send path via the reactions API) so the type system matches what actually works at runtime. The *ActivityInput classes themselves are preserved for model symmetry; only the union membership changes. Co-Authored-By: Claude Opus 4.6 (1M context) --- .../api/activities/activity_params.py | 23 ++++--------------- 1 file changed, 5 insertions(+), 18 deletions(-) diff --git a/packages/api/src/microsoft_teams/api/activities/activity_params.py b/packages/api/src/microsoft_teams/api/activities/activity_params.py index a566f263..66282d46 100644 --- a/packages/api/src/microsoft_teams/api/activities/activity_params.py +++ b/packages/api/src/microsoft_teams/api/activities/activity_params.py @@ -3,38 +3,25 @@ Licensed under the MIT License. """ -# Union of all activity input types (each defined next to their respective activities) +# Union of activity input types that APX actually accepts on outbound send. +# Other *ActivityInput classes exist for model symmetry but are not sendable — +# the Teams service rejects them (messageDelete, messageUpdate, etc. +# are inbound-only event notifications, not outbound activity types). from typing import Annotated, Union from pydantic import Field -from .command import CommandResultActivityInput, CommandSendActivityInput -from .conversation import ConversationUpdateActivityInput -from .handoff import HandoffActivityInput from .message import ( MessageActivityInput, - MessageDeleteActivityInput, MessageReactionActivityInput, - MessageUpdateActivityInput, ) -from .trace import TraceActivityInput from .typing import TypingActivityInput ActivityParams = Annotated[ Union[ - # Simple activities - ConversationUpdateActivityInput, - HandoffActivityInput, - TraceActivityInput, - TypingActivityInput, - # Message activities MessageActivityInput, - MessageDeleteActivityInput, MessageReactionActivityInput, - MessageUpdateActivityInput, - # Command activities - CommandSendActivityInput, - CommandResultActivityInput, + TypingActivityInput, ], Field(discriminator="type"), ] From d84ca9bc020f50dfc92a4d01cb3840a925d2dddd Mon Sep 17 00:00:00 2001 From: Rajan Date: Tue, 31 Mar 2026 18:36:52 -0400 Subject: [PATCH 2/3] =?UTF-8?q?fix:=20address=20review=20=E2=80=94=20clari?= =?UTF-8?q?fy=20comment,=20drop=20APX=20reference?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Rewrite header comment to explain the union's purpose without internal terminology. Explicitly call out MessageReactionActivityInput as a temporary inclusion pending its own send path. Co-Authored-By: Claude Opus 4.6 (1M context) --- .../microsoft_teams/api/activities/activity_params.py | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/packages/api/src/microsoft_teams/api/activities/activity_params.py b/packages/api/src/microsoft_teams/api/activities/activity_params.py index 66282d46..ede6d342 100644 --- a/packages/api/src/microsoft_teams/api/activities/activity_params.py +++ b/packages/api/src/microsoft_teams/api/activities/activity_params.py @@ -3,10 +3,13 @@ Licensed under the MIT License. """ -# Union of activity input types that APX actually accepts on outbound send. -# Other *ActivityInput classes exist for model symmetry but are not sendable — -# the Teams service rejects them (messageDelete, messageUpdate, etc. -# are inbound-only event notifications, not outbound activity types). +# Union of activity input types that can be sent via the +# /v3/conversations/{id}/activities create/update endpoints. +# Other *ActivityInput classes exist for model symmetry but represent +# inbound-only event notifications (messageDelete, messageUpdate, etc.). +# NOTE: MessageReactionActivityInput is temporarily included here even +# though reactions have a dedicated /activities/{id}/reactions endpoint +# exposed via ReactionClient. from typing import Annotated, Union from pydantic import Field From c4f4b4bbd39691c2d5aa7de10c6e278f38ac9a29 Mon Sep 17 00:00:00 2001 From: lilydu Date: Fri, 3 Apr 2026 11:04:42 -0700 Subject: [PATCH 3/3] updated --- .../api/activities/__init__.py | 6 -- .../api/activities/activity_params.py | 8 +-- .../api/activities/command/__init__.py | 6 +- .../api/activities/command/command_result.py | 8 +-- .../api/activities/command/command_send.py | 8 +-- .../api/activities/conversation/__init__.py | 2 - .../conversation/conversation_update.py | 8 +-- .../microsoft_teams/api/activities/handoff.py | 22 ------- .../api/activities/message/__init__.py | 5 +- .../api/activities/message/message_delete.py | 6 +- .../api/activities/message/message_update.py | 60 +------------------ .../microsoft_teams/api/activities/trace.py | 52 ---------------- .../apps/routing/activity_route_configs.py | 16 ----- .../apps/routing/generated_handlers.py | 40 ------------- 14 files changed, 10 insertions(+), 237 deletions(-) delete mode 100644 packages/api/src/microsoft_teams/api/activities/handoff.py delete mode 100644 packages/api/src/microsoft_teams/api/activities/trace.py diff --git a/packages/api/src/microsoft_teams/api/activities/__init__.py b/packages/api/src/microsoft_teams/api/activities/__init__.py index be3f19c3..1050c818 100644 --- a/packages/api/src/microsoft_teams/api/activities/__init__.py +++ b/packages/api/src/microsoft_teams/api/activities/__init__.py @@ -18,7 +18,6 @@ ) from .event import * # noqa: F403 from .event import EventActivity -from .handoff import HandoffActivity from .install_update import * # noqa: F403 from .install_update import InstallUpdateActivity from .invoke import * # noqa: F403 @@ -26,13 +25,10 @@ from .message import * # noqa: F403 from .message import MessageActivities from .sent_activity import SentActivity -from .trace import TraceActivity from .typing import TypingActivity, TypingActivityInput Activity = Annotated[ Union[ - HandoffActivity, - TraceActivity, TypingActivity, CommandActivity, ConversationActivity, @@ -61,13 +57,11 @@ "ConversationUpdateActivity", "ConversationChannelData", "EventActivity", - "HandoffActivity", "InstallUpdateActivity", "TypingActivity", "TypingActivityInput", "ConversationEventType", "InvokeActivity", - "TraceActivity", "ActivityParams", "SentActivity", ] diff --git a/packages/api/src/microsoft_teams/api/activities/activity_params.py b/packages/api/src/microsoft_teams/api/activities/activity_params.py index ede6d342..bed4be58 100644 --- a/packages/api/src/microsoft_teams/api/activities/activity_params.py +++ b/packages/api/src/microsoft_teams/api/activities/activity_params.py @@ -3,13 +3,7 @@ Licensed under the MIT License. """ -# Union of activity input types that can be sent via the -# /v3/conversations/{id}/activities create/update endpoints. -# Other *ActivityInput classes exist for model symmetry but represent -# inbound-only event notifications (messageDelete, messageUpdate, etc.). -# NOTE: MessageReactionActivityInput is temporarily included here even -# though reactions have a dedicated /activities/{id}/reactions endpoint -# exposed via ReactionClient. +# Union of all activity input types (each defined next to their respective activities) from typing import Annotated, Union from pydantic import Field diff --git a/packages/api/src/microsoft_teams/api/activities/command/__init__.py b/packages/api/src/microsoft_teams/api/activities/command/__init__.py index 1d8eea47..2b73f0b2 100644 --- a/packages/api/src/microsoft_teams/api/activities/command/__init__.py +++ b/packages/api/src/microsoft_teams/api/activities/command/__init__.py @@ -7,17 +7,15 @@ from pydantic import Field -from .command_result import CommandResultActivity, CommandResultActivityInput, CommandResultValue -from .command_send import CommandSendActivity, CommandSendActivityInput, CommandSendValue +from .command_result import CommandResultActivity, CommandResultValue +from .command_send import CommandSendActivity, CommandSendValue CommandActivity = Annotated[Union[CommandSendActivity, CommandResultActivity], Field(discriminator="type")] __all__ = [ "CommandResultValue", "CommandResultActivity", - "CommandResultActivityInput", "CommandSendValue", "CommandSendActivity", - "CommandSendActivityInput", "CommandActivity", ] diff --git a/packages/api/src/microsoft_teams/api/activities/command/command_result.py b/packages/api/src/microsoft_teams/api/activities/command/command_result.py index 5d32ceeb..1cb62d57 100644 --- a/packages/api/src/microsoft_teams/api/activities/command/command_result.py +++ b/packages/api/src/microsoft_teams/api/activities/command/command_result.py @@ -5,7 +5,7 @@ from typing import Any, Literal, Optional -from ...models import ActivityBase, ActivityInputBase, CustomBaseModel +from ...models import ActivityBase, CustomBaseModel class CommandResultValue(CustomBaseModel): @@ -45,9 +45,3 @@ class CommandResultActivity(_CommandResultBase, ActivityBase): name: str # pyright: ignore [reportGeneralTypeIssues, reportIncompatibleVariableOverride] """The name of the event.""" - - -class CommandResultActivityInput(_CommandResultBase, ActivityInputBase): - """Input model for creating command result activities with builder methods.""" - - pass diff --git a/packages/api/src/microsoft_teams/api/activities/command/command_send.py b/packages/api/src/microsoft_teams/api/activities/command/command_send.py index 0f5a17fd..81ef8f66 100644 --- a/packages/api/src/microsoft_teams/api/activities/command/command_send.py +++ b/packages/api/src/microsoft_teams/api/activities/command/command_send.py @@ -5,7 +5,7 @@ from typing import Any, Literal, Optional -from ...models import ActivityBase, ActivityInputBase, CustomBaseModel +from ...models import ActivityBase, CustomBaseModel class CommandSendValue(CustomBaseModel): @@ -41,9 +41,3 @@ class CommandSendActivity(_CommandSendBase, ActivityBase): name: str # pyright: ignore [reportGeneralTypeIssues, reportIncompatibleVariableOverride] """The name of the event.""" - - -class CommandSendActivityInput(_CommandSendBase, ActivityInputBase): - """Input model for creating command send activities with builder methods.""" - - pass diff --git a/packages/api/src/microsoft_teams/api/activities/conversation/__init__.py b/packages/api/src/microsoft_teams/api/activities/conversation/__init__.py index d6aa67be..51b90e6a 100644 --- a/packages/api/src/microsoft_teams/api/activities/conversation/__init__.py +++ b/packages/api/src/microsoft_teams/api/activities/conversation/__init__.py @@ -7,7 +7,6 @@ ConversationChannelData, ConversationEventType, ConversationUpdateActivity, - ConversationUpdateActivityInput, ) ConversationActivity = ConversationUpdateActivity @@ -16,6 +15,5 @@ "ConversationEventType", "ConversationChannelData", "ConversationUpdateActivity", - "ConversationUpdateActivityInput", "ConversationActivity", ] diff --git a/packages/api/src/microsoft_teams/api/activities/conversation/conversation_update.py b/packages/api/src/microsoft_teams/api/activities/conversation/conversation_update.py index c13aa338..ba0b6938 100644 --- a/packages/api/src/microsoft_teams/api/activities/conversation/conversation_update.py +++ b/packages/api/src/microsoft_teams/api/activities/conversation/conversation_update.py @@ -5,7 +5,7 @@ from typing import List, Literal, Optional -from ...models import Account, ActivityBase, ActivityInputBase, ChannelData, CustomBaseModel +from ...models import Account, ActivityBase, ChannelData, CustomBaseModel ConversationEventType = Literal[ "channelCreated", @@ -53,9 +53,3 @@ class ConversationUpdateActivity(_ConversationUpdateBase, ActivityBase): channel_data: ConversationChannelData # pyright: ignore [reportGeneralTypeIssues, reportIncompatibleVariableOverride] """Channel data with event type information.""" - - -class ConversationUpdateActivityInput(_ConversationUpdateBase, ActivityInputBase): - """Input model for creating conversation update activities with builder methods.""" - - pass diff --git a/packages/api/src/microsoft_teams/api/activities/handoff.py b/packages/api/src/microsoft_teams/api/activities/handoff.py deleted file mode 100644 index 065b538a..00000000 --- a/packages/api/src/microsoft_teams/api/activities/handoff.py +++ /dev/null @@ -1,22 +0,0 @@ -""" -Copyright (c) Microsoft Corporation. All rights reserved. -Licensed under the MIT License. -""" - -from typing import Literal - -from ..models import ActivityBase, ActivityInputBase, CustomBaseModel - - -class _HandoffBase(CustomBaseModel): - """Base class containing shared handoff activity fields (all Optional except type).""" - - type: Literal["handoff"] = "handoff" - - -class HandoffActivity(_HandoffBase, ActivityBase): - """Output model for received handoff activities with required fields and read-only properties.""" - - -class HandoffActivityInput(_HandoffBase, ActivityInputBase): - """Input model for creating handoff activities with builder methods.""" diff --git a/packages/api/src/microsoft_teams/api/activities/message/__init__.py b/packages/api/src/microsoft_teams/api/activities/message/__init__.py index a6ad3a6c..2bbfffa1 100644 --- a/packages/api/src/microsoft_teams/api/activities/message/__init__.py +++ b/packages/api/src/microsoft_teams/api/activities/message/__init__.py @@ -8,12 +8,11 @@ from pydantic import Field from .message import MessageActivity, MessageActivityInput -from .message_delete import MessageDeleteActivity, MessageDeleteActivityInput, MessageDeleteChannelData +from .message_delete import MessageDeleteActivity, MessageDeleteChannelData from .message_reaction import MessageReactionActivity, MessageReactionActivityInput from .message_update import ( MessageEventType, MessageUpdateActivity, - MessageUpdateActivityInput, MessageUpdateChannelData, ) @@ -32,12 +31,10 @@ "MessageActivity", "MessageActivityInput", "MessageDeleteActivity", - "MessageDeleteActivityInput", "MessageDeleteChannelData", "MessageReactionActivity", "MessageReactionActivityInput", "MessageUpdateActivity", - "MessageUpdateActivityInput", "MessageUpdateChannelData", "MessageEventType", ] diff --git a/packages/api/src/microsoft_teams/api/activities/message/message_delete.py b/packages/api/src/microsoft_teams/api/activities/message/message_delete.py index 7f7272f0..ee203ccc 100644 --- a/packages/api/src/microsoft_teams/api/activities/message/message_delete.py +++ b/packages/api/src/microsoft_teams/api/activities/message/message_delete.py @@ -5,7 +5,7 @@ from typing import Literal, Optional -from ...models import ActivityBase, ActivityInputBase, ChannelData +from ...models import ActivityBase, ChannelData from ...models.custom_base_model import CustomBaseModel @@ -30,7 +30,3 @@ class MessageDeleteActivity(_MessageDeleteBase, ActivityBase): channel_data: MessageDeleteChannelData # pyright: ignore [reportGeneralTypeIssues] """Channel-specific data for message delete events.""" - - -class MessageDeleteActivityInput(_MessageDeleteBase, ActivityInputBase): - """Input model for creating message delete activities with builder methods.""" diff --git a/packages/api/src/microsoft_teams/api/activities/message/message_update.py b/packages/api/src/microsoft_teams/api/activities/message/message_update.py index 78f6ab80..d0d00cc7 100644 --- a/packages/api/src/microsoft_teams/api/activities/message/message_update.py +++ b/packages/api/src/microsoft_teams/api/activities/message/message_update.py @@ -4,9 +4,9 @@ """ from datetime import datetime -from typing import Any, Literal, Optional, Self +from typing import Any, Literal, Optional -from ...models import ActivityBase, ActivityInputBase, ChannelData +from ...models import ActivityBase, ChannelData from ...models.custom_base_model import CustomBaseModel MessageEventType = Literal["undeleteMessage", "editMessage"] @@ -54,59 +54,3 @@ class MessageUpdateActivity(_MessageUpdateBase, ActivityBase): channel_data: MessageUpdateChannelData # pyright: ignore [reportGeneralTypeIssues] """Channel-specific data for message update events.""" - - -class MessageUpdateActivityInput(_MessageUpdateBase, ActivityInputBase): - """Input model for creating message update activities with builder methods.""" - - def with_text(self, text: str) -> Self: - """ - Set the text content of the message. - - Args: - text: The text content to set - - Returns: - Self for method chaining - """ - self.text = text - return self - - def with_speak(self, speak: str) -> Self: - """ - Set the text to speak. - - Args: - speak: The text to speak - - Returns: - Self for method chaining - """ - self.speak = speak - return self - - def with_summary(self, summary: str) -> Self: - """ - Set the summary text. - - Args: - summary: The summary text to set - - Returns: - Self for method chaining - """ - self.summary = summary - return self - - def with_expiration(self, expiration: datetime) -> Self: - """ - Set the expiration time for the activity. - - Args: - expiration: The expiration datetime to set - - Returns: - Self for method chaining - """ - self.expiration = expiration - return self diff --git a/packages/api/src/microsoft_teams/api/activities/trace.py b/packages/api/src/microsoft_teams/api/activities/trace.py deleted file mode 100644 index ff12876a..00000000 --- a/packages/api/src/microsoft_teams/api/activities/trace.py +++ /dev/null @@ -1,52 +0,0 @@ -""" -Copyright (c) Microsoft Corporation. All rights reserved. -Licensed under the MIT License. -""" - -from typing import Any, Literal, Optional - -from ..models import ActivityBase, ActivityInputBase, CustomBaseModel - - -class _TraceBase(CustomBaseModel): - """Base class containing shared trace activity fields (all Optional except type).""" - - type: Literal["trace"] = "trace" - - name: Optional[str] = None - """" - The name of the operation associated with an invoke or event activity. - """ - - label: Optional[str] = None - """ - A descriptive label for the activity. - """ - - value_type: Optional[str] = None - """ - The type of the activity's value object. - """ - - value: Optional[Any] = None - """ - A value that is associated with the activity. - """ - - -class TraceActivity(_TraceBase, ActivityBase): - """Output model for received trace activities with required fields and read-only properties.""" - - label: str # pyright: ignore [reportGeneralTypeIssues] - """ - A descriptive label for the activity. - """ - - value_type: str # pyright: ignore [reportGeneralTypeIssues] - """ - The type of the activity's value object. - """ - - -class TraceActivityInput(_TraceBase, ActivityInputBase): - """Input model for creating trace activities with builder methods.""" diff --git a/packages/apps/src/microsoft_teams/apps/routing/activity_route_configs.py b/packages/apps/src/microsoft_teams/apps/routing/activity_route_configs.py index e9084e73..52854141 100644 --- a/packages/apps/src/microsoft_teams/apps/routing/activity_route_configs.py +++ b/packages/apps/src/microsoft_teams/apps/routing/activity_route_configs.py @@ -19,7 +19,6 @@ ExecuteActionInvokeActivity, FileConsentInvokeActivity, HandoffActionInvokeActivity, - HandoffActivity, InstalledActivity, InvokeActivity, MeetingEndEventActivity, @@ -52,7 +51,6 @@ TaskFetchInvokeActivity, TaskModuleInvokeResponse, TaskSubmitInvokeActivity, - TraceActivity, TypingActivity, UninstalledActivity, ) @@ -551,20 +549,6 @@ class ActivityConfig: selector=lambda activity: isinstance(activity, TypingActivity), output_model=None, ), - "trace": ActivityConfig( - name="trace", - method_name="on_trace", - input_model=TraceActivity, - selector=lambda activity: isinstance(activity, TraceActivity), - output_model=None, - ), - "handoff": ActivityConfig( - name="handoff", - method_name="on_handoff", - input_model=HandoffActivity, - selector=lambda activity: isinstance(activity, HandoffActivity), - output_model=None, - ), # Generic Activity Handler (catch-all) "activity": ActivityConfig( name="activity", diff --git a/packages/apps/src/microsoft_teams/apps/routing/generated_handlers.py b/packages/apps/src/microsoft_teams/apps/routing/generated_handlers.py index 1b1ae7ad..8f99b091 100644 --- a/packages/apps/src/microsoft_teams/apps/routing/generated_handlers.py +++ b/packages/apps/src/microsoft_teams/apps/routing/generated_handlers.py @@ -24,7 +24,6 @@ ExecuteActionInvokeActivity, FileConsentInvokeActivity, HandoffActionInvokeActivity, - HandoffActivity, InstalledActivity, InstallUpdateActivity, InvokeActivity, @@ -54,7 +53,6 @@ TabSubmitInvokeActivity, TaskFetchInvokeActivity, TaskSubmitInvokeActivity, - TraceActivity, TypingActivity, UninstalledActivity, ) @@ -1632,44 +1630,6 @@ def decorator(func: BasicHandler[TypingActivity]) -> BasicHandler[TypingActivity return decorator(handler) return decorator - @overload - def on_trace(self, handler: BasicHandler[TraceActivity]) -> BasicHandler[TraceActivity]: ... - - @overload - def on_trace(self) -> Callable[[BasicHandler[TraceActivity]], BasicHandler[TraceActivity]]: ... - - def on_trace(self, handler: Optional[BasicHandler[TraceActivity]] = None) -> BasicHandlerUnion[TraceActivity]: - """Register a trace activity handler.""" - - def decorator(func: BasicHandler[TraceActivity]) -> BasicHandler[TraceActivity]: - validate_handler_type(func, TraceActivity, "on_trace", "TraceActivity") - config = ACTIVITY_ROUTES["trace"] - self.router.add_handler(config.selector, func) - return func - - if handler is not None: - return decorator(handler) - return decorator - - @overload - def on_handoff(self, handler: BasicHandler[HandoffActivity]) -> BasicHandler[HandoffActivity]: ... - - @overload - def on_handoff(self) -> Callable[[BasicHandler[HandoffActivity]], BasicHandler[HandoffActivity]]: ... - - def on_handoff(self, handler: Optional[BasicHandler[HandoffActivity]] = None) -> BasicHandlerUnion[HandoffActivity]: - """Register a handoff activity handler.""" - - def decorator(func: BasicHandler[HandoffActivity]) -> BasicHandler[HandoffActivity]: - validate_handler_type(func, HandoffActivity, "on_handoff", "HandoffActivity") - config = ACTIVITY_ROUTES["handoff"] - self.router.add_handler(config.selector, func) - return func - - if handler is not None: - return decorator(handler) - return decorator - @overload def on_activity(self, handler: BasicHandler[Activity]) -> BasicHandler[Activity]: ...