Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
52 commits
Select commit Hold shift + click to select a range
1bfd426
Updating typing annotations
rodrigobr-msft Jun 16, 2026
c8a4dfa
Another commit
rodrigobr-msft Jun 17, 2026
05f00e7
Separating route hubs
rodrigobr-msft Jun 17, 2026
4290f74
Adding file consent routes
rodrigobr-msft Jun 17, 2026
7537bc2
Adding refactored configuration routes
rodrigobr-msft Jun 17, 2026
50867ec
Refactor Team routes
rodrigobr-msft Jun 17, 2026
130b7ab
Cleaned up TeamsAgentExtension
rodrigobr-msft Jun 17, 2026
f3d1b31
Removing duplicate logic
rodrigobr-msft Jun 17, 2026
c0ed7b0
Cleaning up extension and adding new route handler protocol for Handoff
rodrigobr-msft Jun 18, 2026
b04bcaa
Addressing merge conflicts
rodrigobr-msft Jun 18, 2026
d1a4371
Further cleanup and formatting
rodrigobr-msft Jun 18, 2026
22a280e
Fixing teams models imports
rodrigobr-msft Jun 18, 2026
7a37f95
Adding direct decorator support for route hooks without positional ar…
rodrigobr-msft Jun 18, 2026
5758dac
Adding missing route hooks
rodrigobr-msft Jun 22, 2026
a11e18a
Adding _StateContra for proper route handler definitions
rodrigobr-msft Jun 22, 2026
9ff8559
Revisions to TeamsInfo file
rodrigobr-msft Jun 22, 2026
c166ca9
Adding targeted activity functionality to TeamsTurnContext
rodrigobr-msft Jun 22, 2026
dc0e763
Modification to test sample
rodrigobr-msft Jun 22, 2026
d1f9c5d
Adding back in ActivityHandler extension and fixing TeamsInfo tests
rodrigobr-msft Jun 22, 2026
97b8aed
Authorization.connection_manager
rodrigobr-msft Jun 22, 2026
1981797
renaming teams to msteams
rodrigobr-msft Jun 23, 2026
5fc9e37
Renaming teams to msteams
rodrigobr-msft Jun 23, 2026
d933753
Resolving merge conflicts
rodrigobr-msft Jun 23, 2026
f663e4c
Another commit
rodrigobr-msft Jun 23, 2026
ec17f2c
Merge branch 'main' of https://github.com/microsoft/Agents-for-python…
rodrigobr-msft Jun 23, 2026
6349c6c
Revising ApiClient setting
rodrigobr-msft Jun 23, 2026
0495df0
Adding TeamsActivity static helper
rodrigobr-msft Jun 23, 2026
8642094
Graph service client creation
rodrigobr-msft Jun 23, 2026
6bbcdc5
client getters
rodrigobr-msft Jun 24, 2026
5be4b3f
Adding TeamsActivity connection
rodrigobr-msft Jun 24, 2026
2390f12
Bug fixes
rodrigobr-msft Jun 24, 2026
33e2bf3
Fixing unit tests
rodrigobr-msft Jun 24, 2026
d87e2b2
Adding routing integration tests
rodrigobr-msft Jun 24, 2026
f575e59
Updating package README and minor bug
rodrigobr-msft Jun 24, 2026
ac41caf
github workflow edit
rodrigobr-msft Jun 24, 2026
633d143
Updating documentation
rodrigobr-msft Jun 24, 2026
5615194
Addressing PR feedback
rodrigobr-msft Jun 24, 2026
e253f51
Removing test case
rodrigobr-msft Jun 24, 2026
1fb93c4
Updating azdo
rodrigobr-msft Jun 24, 2026
65baedc
UPdating conversation agent
rodrigobr-msft Jun 24, 2026
f3d0964
Reorganizing test_samples
rodrigobr-msft Jun 26, 2026
e629ed8
Finalizing msteams test samples
rodrigobr-msft Jun 26, 2026
0165bda
another commit
rodrigobr-msft Jun 30, 2026
c98f732
Addressing PR feedback
rodrigobr-msft Jun 30, 2026
10ec558
Potential fix for pull request finding
rodrigobr-msft Jun 30, 2026
b658058
Potential fix for pull request finding
rodrigobr-msft Jul 1, 2026
f49f748
Small fixes to typing annotations
rodrigobr-msft Jul 1, 2026
defde2b
Merge branch 'users/robrandao/msteams' of https://github.com/microsof…
rodrigobr-msft Jul 1, 2026
4bd55eb
Better sync of TeamsTurnContext
rodrigobr-msft Jul 6, 2026
26d1bbc
Merge branch 'main' into users/robrandao/msteams
rodrigobr-msft Jul 6, 2026
5bcc591
Replacing ChannelData with TeamInfo and ChannelInfo in route handlers
rodrigobr-msft Jul 6, 2026
908dae7
erge branch 'users/robrandao/msteams' of https://github.com/microsoft…
rodrigobr-msft Jul 6, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions .azdo/ci-pr.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -73,10 +73,10 @@ steps:
python -m pip install ./dist/microsoft_agents_copilotstudio_client*.whl
python -m pip install ./dist/microsoft_agents_hosting_aiohttp*.whl
python -m pip install ./dist/microsoft_agents_hosting_dialogs*.whl
if python -c "import sys; sys.exit(0 if sys.version_info >= (3, 12) else 1)"; then
python -m pip install ./dist/microsoft_agents_hosting_teams*.whl
if python -c "import sys; sys.exit(0 if sys.version_info >= (3, 11) else 1)"; then
python -m pip install ./dist/microsoft_agents_hosting_msteams*.whl
else
echo "Skipping microsoft_agents_hosting_teams: requires Python 3.12+"
echo "Skipping microsoft_agents_hosting_msteams: requires Python 3.11+"
fi
python -m pip install ./dist/microsoft_agents_hosting_slack*.whl
python -m pip install ./dist/microsoft_agents_storage_blob*.whl
Expand Down
6 changes: 3 additions & 3 deletions .github/workflows/python-package.yml
Original file line number Diff line number Diff line change
Expand Up @@ -62,10 +62,10 @@ jobs:
python -m pip install ./dist/microsoft_agents_copilotstudio_client*.whl
python -m pip install ./dist/microsoft_agents_hosting_aiohttp*.whl
python -m pip install ./dist/microsoft_agents_hosting_dialogs*.whl
if python -c "import sys; sys.exit(0 if sys.version_info >= (3, 12) else 1)"; then
python -m pip install ./dist/microsoft_agents_hosting_teams*.whl
if python -c "import sys; sys.exit(0 if sys.version_info >= (3, 11) else 1)"; then
python -m pip install ./dist/microsoft_agents_hosting_msteams*.whl
else
echo "Skipping microsoft_agents_hosting_teams: requires Python 3.12+"
echo "Skipping microsoft_agents_hosting_msteams: requires Python 3.11+"
fi
python -m pip install ./dist/microsoft_agents_hosting_slack*.whl
python -m pip install ./dist/microsoft_agents_storage_blob*.whl
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,8 @@
ProductInfo,
Thing,
StreamInfo,
ActivityTreatment,
ActivityTreatmentTypes,
)
from .error import Error
from .error_response import ErrorResponse
Expand Down Expand Up @@ -199,5 +201,7 @@
"load_configuration_from_env",
"ChannelAdapterProtocol",
"TurnContextProtocol",
"ActivityTreatment",
"ActivityTreatmentTypes",
"TokenOrSignInResourceResponse",
]
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
# Copyright (c) Microsoft Corporation. All rights reserved.
# Licensed under the MIT License.

from .activity_treatment import ActivityTreatment, ActivityTreatmentTypes
from .mention import Mention
from .entity import Entity
from .entity_types import EntityTypes
Expand Down Expand Up @@ -35,4 +36,6 @@
"Place",
"ProductInfo",
"Thing",
"ActivityTreatment",
"ActivityTreatmentTypes",
]
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# Copyright (c) Microsoft Corporation. All rights reserved.
# Licensed under the MIT License.

from enum import Enum
from typing import Literal

from .entity import Entity
from .entity_types import EntityTypes


class ActivityTreatmentTypes(str, Enum):
"""Well-known enumeration of activity treatment types."""

TARGETED = "targeted"


class ActivityTreatment(Entity):
"""Activity treatment information (entity type: "activityTreatment").

:param treatment: The type of treatment
:type treatment: ~microsoft_agents.activity.ActivityTreatmentTypes
:param type: Type of this entity (RFC 3987 IRI)
:type type: str
"""

type: Literal[EntityTypes.ACTIVITY_TREATMENT] = EntityTypes.ACTIVITY_TREATMENT
treatment: ActivityTreatmentTypes
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
class EntityTypes(str, Enum):
"""Well-known enumeration of entity types."""

ACTIVITY_TREATMENT = "activityTreatment"
GEO_COORDINATES = "GeoCoordinates"
MENTION = "mention"
PLACE = "Place"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,9 @@

class RouteHandler(Protocol[StateT]):
def __call__(self, context: TurnContext, state: StateT, /) -> Awaitable[None]: ...


class HandoffHandler(Protocol[StateT]):
def __call__(
self, context: TurnContext, state: StateT, handoff_data: str
) -> Awaitable[None]: ...
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,11 @@
from .typing_indicator import TypingIndicator
from .telemetry import spans

from ._type_defs import RouteHandler, RouteSelector
from ._type_defs import (
RouteHandler,
HandoffHandler,
RouteSelector,
)
from ._routes import _RouteList, _Route, RouteRank, _agentic_selector
from .proactive import Proactive, ProactiveOptions

Expand All @@ -69,7 +73,7 @@ class AgentApplication(Agent, Generic[StateT]):

_options: ApplicationOptions
_adapter: Optional[ChannelServiceAdapter] = None
_auth: Optional[Authorization] = None
_auth: Authorization
_proactive: Optional[Proactive] = None
_internal_before_turn: list[Callable[[TurnContext, StateT], Awaitable[bool]]]
_internal_after_turn: list[Callable[[TurnContext, StateT], Awaitable[bool]]]
Expand Down Expand Up @@ -614,11 +618,11 @@ def __call(func: RouteHandler[StateT]) -> RouteHandler[StateT]:
@overload
def handoff(
self,
func: Callable[[TurnContext, StateT, str], Awaitable[None]],
func: HandoffHandler[StateT],
*,
auth_handlers: Optional[list[str]] = None,
**kwargs,
) -> Callable[[TurnContext, StateT, str], Awaitable[None]]: ...
) -> HandoffHandler[StateT]: ...

@overload
def handoff(
Expand All @@ -627,21 +631,21 @@ def handoff(
auth_handlers: Optional[list[str]] = None,
**kwargs: Any,
) -> Callable[
[Callable[[TurnContext, StateT, str], Awaitable[None]]],
Callable[[TurnContext, StateT, str], Awaitable[None]],
[HandoffHandler[StateT]],
HandoffHandler[StateT],
]: ...

def handoff(
self,
func: Optional[Callable[[TurnContext, StateT, str], Awaitable[None]]] = None,
func: Optional[HandoffHandler[StateT]] = None,
*,
auth_handlers: Optional[list[str]] = None,
**kwargs,
) -> (
Callable[[TurnContext, StateT, str], Awaitable[None]]
HandoffHandler[StateT]
| Callable[
[Callable[[TurnContext, StateT, str], Awaitable[None]]],
Callable[[TurnContext, StateT, str], Awaitable[None]],
[HandoffHandler[StateT]],
HandoffHandler[StateT],
]
):
"""
Expand All @@ -655,7 +659,7 @@ async def on_handoff(context: TurnContext, state: TurnState, continuation: str):
print(continuation)

:param func: Optional handler to register directly without using decorator syntax.
:type func: Optional[Callable[[TurnContext, StateT, str], Awaitable[None]]]
:type func: Optional[HandoffHandler[StateT]]
:param auth_handlers: Optional list of authorization handler IDs for the route.
:type auth_handlers: Optional[list[str]]
:param kwargs: Additional route configuration passed to :meth:`microsoft_agents.hosting.core.AgentApplication.add_route`.
Expand All @@ -668,8 +672,8 @@ def __selector(context: TurnContext) -> bool:
)

def __call(
func: Callable[[TurnContext, StateT, str], Awaitable[None]],
) -> Callable[[TurnContext, StateT, str], Awaitable[None]]:
func: HandoffHandler[StateT],
) -> HandoffHandler[StateT]:
async def __handler(context: TurnContext, state: StateT):
if (
isinstance(context.activity.value, dict)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
from typing import Callable, Optional

from microsoft_agents.hosting.core.app.oauth import AuthHandler
from microsoft_agents.hosting.core.authorization import Connections
from microsoft_agents.hosting.core.storage import Storage
Comment thread
rodrigobr-msft marked this conversation as resolved.

# from .auth import AuthOptions
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ class TurnContext(TurnContextProtocol):
# Same constant as in the BF Adapter, duplicating here to avoid circular dependency
_INVOKE_RESPONSE_KEY = "TurnContext.InvokeResponse"

_activity: Activity

def __init__(
self,
adapter_or_context,
Expand All @@ -43,7 +45,7 @@ def __init__(
self._identity = adapter_or_context.identity
else:
self.adapter = adapter_or_context
self._activity = request
self._activity = request # exception thrown if None further down
self.responses: list[Activity] = []
self._services: dict = {}
self._on_send_activities: Callable[
Expand All @@ -60,7 +62,7 @@ def __init__(

if self.adapter is None:
raise TypeError("TurnContext must be instantiated with an adapter.")
if self.activity is None:
if self._activity is None:
raise TypeError(
"TurnContext must be instantiated with a request parameter of type Activity."
)
Expand All @@ -83,7 +85,7 @@ def copy_to(self, context: "TurnContext") -> None:
"""
for attribute in [
"adapter",
"activity",
"_activity",
"_responded",
"_services",
"_on_send_activities",
Expand Down Expand Up @@ -187,7 +189,7 @@ async def send_activity(
activity_or_text: Activity | str,
speak: str | None = None,
input_hint: str | None = None,
) -> ResourceResponse | None:
) -> ResourceResponse:
"""
Sends a single activity or message to the user.
:param activity_or_text:
Expand All @@ -203,7 +205,7 @@ async def send_activity(
activity_or_text.speak = speak

result = await self.send_activities([activity_or_text])
return result[0] if result else None
return result[0] if result else ResourceResponse()
Comment thread
rodrigobr-msft marked this conversation as resolved.

async def send_activities(
self, activities: list[Activity]
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
# Copyright (c) Microsoft Corporation. All rights reserved.
# Licensed under the MIT License.

"""Microsoft 365 Agents SDK -- Microsoft Teams hosting extension.

This package layers Teams-specific functionality on top of an
:class:`~microsoft_agents.hosting.core.AgentApplication`. Its entry point is
:class:`TeamsAgentExtension`, which exposes namespaced route registration for
Teams events (channels, teams, meetings, messages, message extensions, task
modules, configuration, and file consent). It also provides the Teams-aware
:class:`TeamsTurnContext` and :class:`TeamsActivity` helpers.
"""

from .teams_agent_extension import TeamsAgentExtension
from .channel import Channel
from .config import Config
from .file_consent import FileConsent
from .meeting import Meeting
from .message import Message
from .message_extension import MessageExtension
from .task_module import TaskModule
from .team import Team

from .teams_activity import TeamsActivity
from .teams_turn_context import TeamsTurnContext

__all__ = [
"TeamsAgentExtension",
"Channel",
"Config",
"FileConsent",
"Meeting",
"Message",
"MessageExtension",
"TaskModule",
"Team",
"TeamsActivity",
"TeamsTurnContext",
]
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
# Copyright (c) Microsoft Corporation. All rights reserved.
# Licensed under the MIT License.

"""Microsoft Graph client integration for the Teams hosting layer.

Builds a :class:`msgraph.GraphServiceClient` whose requests are authenticated
with tokens obtained from the agent's configured authorization, so handlers can
call Microsoft Graph on behalf of the current turn.
"""

from typing import Any

from kiota_abstractions.request_information import RequestInformation
from kiota_abstractions.authentication import AuthenticationProvider

from msgraph import GraphServiceClient, GraphRequestAdapter

from microsoft_agents.hosting.core import (
AgentApplication,
TurnContext,
)


class _SDKAuthenticationProvider(AuthenticationProvider):
"""Kiota authentication provider backed by the agent's authorization.

Acquires an access token for the current turn via
:meth:`AgentApplication.auth.get_token` and attaches it to outgoing Graph
requests as a bearer token.
"""

def __init__(
self,
app: AgentApplication,
context: TurnContext,
handler_name: str | None = None,
):
"""Capture the context needed to resolve a token at request time.

:param app: The agent application whose authorization issues tokens.
:param context: The current turn context.
:param handler_name: The auth handler name used to acquire the token.
"""
self._app = app
self._context = context
self._handler_name = handler_name

async def authenticate_request(
self,
request: RequestInformation,
additional_authentication_context: dict[str, Any] | None = None,
) -> None:
"""Attach a bearer token to the outgoing Graph request.

:param request: The request to authenticate.
:param additional_authentication_context: Optional Kiota authentication
context; unused but accepted to satisfy the provider interface.
"""
if additional_authentication_context is None:
additional_authentication_context = {}

token_response = await self._app.auth.get_token(
self._context, self._handler_name
)
if token_response:
request.headers["Authorization"] = f"Bearer {token_response.token}"


def _create_graph_service_client(
app: AgentApplication,
context: TurnContext,
handler_name: str | None = None,
) -> GraphServiceClient:
"""Create a Graph client authenticated for the current turn.

:param app: The agent application whose authorization issues tokens.
:param context: The current turn context.
:param handler_name: Optional auth handler name used to acquire the token.
:return: A :class:`GraphServiceClient` that authenticates each request via
the agent's authorization.
"""
return GraphServiceClient(
request_adapter=GraphRequestAdapter(
_SDKAuthenticationProvider(app, context, handler_name)
)
)
Loading
Loading