diff --git a/changelog.md b/changelog.md
index 1d0fb341..2d014517 100644
--- a/changelog.md
+++ b/changelog.md
@@ -1,3 +1,40 @@
+# Microsoft 365 Agents SDK for Python - Release Notes v1.2.0
+
+**Release Date:** 2026-07-17
+**Previous Version:** 1.1.0 (Released 2026-06-19)
+
+This release adds first-class Microsoft Teams hosting APIs and a credential-free Entra ID authentication provider, while improving turn processing, OAuth token handling, streaming responses, and async runtime behavior.
+
+## Major Features & Enhancements
+
+- **Teams Hosting Package**: Added the new `microsoft-agents-hosting-msteams` package for building Teams agents with typed, decorator-based routing. It introduces `TeamsAgentExtension`, `TeamsTurnContext`, route namespaces for Teams invoke and event activities, and Teams API client access from handlers (#434)
+- **Entra Auth Sidecar Package**: Added the new `microsoft-agents-authentication-entra-auth-sidecar` package for credential-free Entra ID Agent ID authentication through the Microsoft Entra ID Agent Container sidecar. It includes `SidecarAuth`, `SidecarHttpClient`, `SidecarConnectionSettings`, in-memory token caching, transient retry handling, and SSRF-safe sidecar URL validation (#424)
+- **AgentApplication Lifecycle Hooks**: Added `before_turn` and `after_turn` hooks for app-level logic around route execution without requiring full middleware. Hooks can short-circuit processing, participate in state saving, and support scenarios such as app-level guards, telemetry enrichment, and turn cleanup (#431)
+- **AgentApplication Routing and Connections**: Improved route registration and exposed `AgentApplication.connection_manager` for code that needs access to configured connections (#429, #432)
+- **Detached SSO Token Exchange**: Teams SSO token exchange invoke activities are now handled independently from regular route dispatch, reducing duplicate route logic and making SSO callbacks more reliable (#469)
+
+## New Models & APIs
+
+- **StreamingResponse Attachments and Reset**: Added `StreamingResponse.add_attachment()` for final-message attachments and `StreamingResponse.reset()` for returning the helper to its initial state after a stream completes (#426)
+- **Activity Method Type Handling**: Improved `Activity` helper methods for typed activity data and entity conversions, including support for the new `ActivityTreatment` entity type (#441)
+- **Configuration Coercion Helpers**: Added shared boolean, integer, and float coercion helpers so environment-derived configuration values are interpreted consistently (#424)
+
+## Bug Fixes
+
+- **State Loading**: Removed duplicate `turn_state.load()` calls and corrected when state is loaded during `AgentApplication` initialization (#455, #460)
+- **Middleware Pipeline**: Fixed `MiddlewareSet` registration and turn invocation so multiple middleware components are registered and invoked in order (#461)
+- **Composite Channels**: Normalized composite channel IDs for all user token operations, improving OAuth behavior for Copilot Web and Teams channel IDs such as `msteams:copilot-web` (#457)
+- **Teams Activity Hooks**: Fixed `TeamsActivityHandler` hook dispatch so Teams-specific handlers receive the required arguments (#450)
+- **Concurrency**: Replaced runtime use of `threading.Lock` with `asyncio.Lock` in async execution paths to avoid blocking the event loop (#442)
+- **Dependencies**: Removed the unused `azure-core` dependency from `microsoft-agents-hosting-core` (#467)
+
+## Developer Experience
+
+- **Logging**: Added color-coded log messages and simplified `ApplicationOptions` by removing the logger option in favor of standard module-level logging (#439, #443)
+- **Samples and Documentation**: Fixed the FastAPI sample startup path and added missing dialogs package links to README release tables (#417, #458)
+
+---
+
# Microsoft 365 Agents SDK for Python - Release Notes v1.1.0
**Release Date:** 2026-06-19
@@ -16,8 +53,6 @@
## Developer Experience
-- **Integration Testing**: Integration testing enhancements (#408)
-- **Testing Initialization Helper**: New testing initialization helper (#416)
- **Typing Annotations**: Updated typing annotations across the codebase (#418)
---
@@ -25,12 +60,11 @@
# Microsoft 365 Agents SDK for Python - Release Notes v1.0.0
**Release Date:** May 22, 2026
-**Previous Version:** 0.9.0 (Released April 15, 2026)
+**Previous Version:** 0.9.1 (Released May 4, 2026)
## Major Features & Enhancements
- **Dialogs Library**: New `microsoft-agents-hosting-dialogs` package porting the BotFramework dialog system (WaterfallDialog, ComponentDialog, DialogSet, DialogContext) and prompts (TextPrompt, NumberPrompt, ChoicePrompt, ConfirmPrompt, DateTimePrompt, AttachmentPrompt, OAuthPrompt) to the new SDK (#378)
-- **Teams SSO Consent**: Support for the Teams SSO `Consent Required` flow (#380)
- **Teams Extension Updates**: `TeamsAgentExtension` and related types re-exported at the `microsoft_agents.hosting.teams` package root, integration with `microsoft-teams-api` 2.x Pydantic models, and new runnable samples for message extensions, task modules, and meeting events (#385)
- **Copilot Web OAuth Base-Channel Support**: New optional `force_base_channel` parameter on `Activity.get_conversation_reference()` and `_OAuthFlow.begin_flow()` so OAuth token exchange uses the base channel (e.g. `msteams`) when receiving composite channel IDs like `msteams:copilot-web` (#392)
- **Per-Channel Typing Indicator**: New `TypingChannelStrategy` and `TypingOptions` for configurable per-channel typing behavior, surfaced via `ApplicationOptions` and a streaming fix (#398)
@@ -39,9 +73,18 @@
## Developer Experience
- **API Reference Docstrings**: Docstrings updated from short-form to fully qualified object names for Learn API reference generation (#381)
+
+---
+
+# Microsoft 365 Agents SDK for Python - Release Notes v0.9.1
+
+**Release Date:** 2026-05-04
+**Previous Version:** 0.9.0 (Released 2026-04-15)
+
+## Enhancements
+
+- **Teams SSO Consent**: Support for the Teams SSO `Consent Required` flow (#380)
- **SuggestedActions Factory**: New factory for the `to` property of `SuggestedActions` (#370)
-- **Dialogs Test Layout**: `hosting-dialogs` tests moved back under the top-level `tests/` directory for consistency with other packages (#379)
-- **Build Dependencies**: Temporary fix for build testing dependencies (#404)
---
diff --git a/libraries/microsoft-agents-activity/microsoft_agents/activity/config/_load_configuration.py b/libraries/microsoft-agents-activity/microsoft_agents/activity/config/_load_configuration.py
index ef0de9f3..9d54c740 100644
--- a/libraries/microsoft-agents-activity/microsoft_agents/activity/config/_load_configuration.py
+++ b/libraries/microsoft-agents-activity/microsoft_agents/activity/config/_load_configuration.py
@@ -33,5 +33,5 @@ def load_configuration_from_env(env_vars: dict[str, Any]) -> dict:
return {
"AGENTAPPLICATION": result.get("AGENTAPPLICATION", {}),
"CONNECTIONS": result.get("CONNECTIONS", {}),
- "CONNECTIONSMAP": result.get("CONNECTIONSMAP", {}),
+ "CONNECTIONSMAP": result.get("CONNECTIONSMAP", []),
}
diff --git a/libraries/microsoft-agents-activity/readme.md b/libraries/microsoft-agents-activity/readme.md
index 2b7d3e77..fc7ccb30 100644
--- a/libraries/microsoft-agents-activity/readme.md
+++ b/libraries/microsoft-agents-activity/readme.md
@@ -15,6 +15,15 @@ This library is part of the **Microsoft 365 Agents SDK for Python** - a comprehe
Date |
Release Notes |
+
+ | 1.2.0 |
+ 2026-07-17 |
+
+
+ 1.2.0 Release Notes
+
+ |
+
| 1.1.0 |
2026-06-19 |
@@ -33,6 +42,15 @@ This library is part of the **Microsoft 365 Agents SDK for Python** - a comprehe
+
+ | 0.9.1 |
+ 2026-05-04 |
+
+
+ 0.9.1 Release Notes
+
+ |
+
| 0.9.0 |
2026-04-15 |
diff --git a/libraries/microsoft-agents-authentication-entra-auth-sidecar/readme.md b/libraries/microsoft-agents-authentication-entra-auth-sidecar/readme.md
index b113baa0..b01260bf 100644
--- a/libraries/microsoft-agents-authentication-entra-auth-sidecar/readme.md
+++ b/libraries/microsoft-agents-authentication-entra-auth-sidecar/readme.md
@@ -7,6 +7,25 @@ Entra ID Agent Container** (the *sidecar*). Instead of acquiring tokens directly
the SDK delegates token acquisition to the sidecar's HTTP API, so the agent process never
handles secrets, certificates, or keys.
+## Release Notes
+
+
+
## Why a sidecar?
- **Credential-free agent code** — all credential management (Managed Identity, Workload
diff --git a/libraries/microsoft-agents-authentication-msal/readme.md b/libraries/microsoft-agents-authentication-msal/readme.md
index 76cfcc39..44087d89 100644
--- a/libraries/microsoft-agents-authentication-msal/readme.md
+++ b/libraries/microsoft-agents-authentication-msal/readme.md
@@ -15,6 +15,15 @@ This library is part of the **Microsoft 365 Agents SDK for Python** - a comprehe
Date |
Release Notes |
+
+ | 1.2.0 |
+ 2026-07-17 |
+
+
+ 1.2.0 Release Notes
+
+ |
+
| 1.1.0 |
2026-06-19 |
@@ -33,6 +42,15 @@ This library is part of the **Microsoft 365 Agents SDK for Python** - a comprehe
+
+ | 0.9.1 |
+ 2026-05-04 |
+
+
+ 0.9.1 Release Notes
+
+ |
+
| 0.9.0 |
2026-04-15 |
diff --git a/libraries/microsoft-agents-copilotstudio-client/readme.md b/libraries/microsoft-agents-copilotstudio-client/readme.md
index e57d135c..ed014f81 100644
--- a/libraries/microsoft-agents-copilotstudio-client/readme.md
+++ b/libraries/microsoft-agents-copilotstudio-client/readme.md
@@ -16,6 +16,15 @@ This library is part of the **Microsoft 365 Agents SDK for Python** - a comprehe
Date |
Release Notes |
+
+ | 1.2.0 |
+ 2026-07-17 |
+
+
+ 1.2.0 Release Notes
+
+ |
+
| 1.1.0 |
2026-06-19 |
@@ -34,6 +43,15 @@ This library is part of the **Microsoft 365 Agents SDK for Python** - a comprehe
+
+ | 0.9.1 |
+ 2026-05-04 |
+
+
+ 0.9.1 Release Notes
+
+ |
+
| 0.9.0 |
2026-04-15 |
diff --git a/libraries/microsoft-agents-hosting-aiohttp/readme.md b/libraries/microsoft-agents-hosting-aiohttp/readme.md
index e6a8e624..a882dc75 100644
--- a/libraries/microsoft-agents-hosting-aiohttp/readme.md
+++ b/libraries/microsoft-agents-hosting-aiohttp/readme.md
@@ -17,6 +17,15 @@ This library is part of the **Microsoft 365 Agents SDK for Python** - a comprehe
Date |
Release Notes |
+
+ | 1.2.0 |
+ 2026-07-17 |
+
+
+ 1.2.0 Release Notes
+
+ |
+
| 1.1.0 |
2026-06-19 |
@@ -35,6 +44,15 @@ This library is part of the **Microsoft 365 Agents SDK for Python** - a comprehe
+
+ | 0.9.1 |
+ 2026-05-04 |
+
+
+ 0.9.1 Release Notes
+
+ |
+
| 0.9.0 |
2026-04-15 |
diff --git a/libraries/microsoft-agents-hosting-core/microsoft_agents/hosting/core/app/oauth/authorization.py b/libraries/microsoft-agents-hosting-core/microsoft_agents/hosting/core/app/oauth/authorization.py
index 335d5b4f..51cc75bc 100644
--- a/libraries/microsoft-agents-hosting-core/microsoft_agents/hosting/core/app/oauth/authorization.py
+++ b/libraries/microsoft-agents-hosting-core/microsoft_agents/hosting/core/app/oauth/authorization.py
@@ -93,16 +93,15 @@ def __init__(
)
if not auth_handlers:
# get from config
- handlers_config: dict[str, dict] = auth_configuration.get("HANDLERS")
- if not auth_handlers and handlers_config:
- auth_handlers = {
- handler_name: AuthHandler(
- name=handler_name,
- auth_type=config.get("TYPE", None),
- **config.get("SETTINGS", {}),
- )
- for handler_name, config in handlers_config.items()
- }
+ handlers_config: dict[str, dict] = auth_configuration.get("HANDLERS") or {}
+ auth_handlers = {
+ handler_name: AuthHandler(
+ name=handler_name,
+ auth_type=config.get("TYPE", ""),
+ **config.get("SETTINGS", {}),
+ )
+ for handler_name, config in handlers_config.items()
+ }
self._handler_settings = auth_handlers
self._auto_sign_in = auto_sign_in or bool(
diff --git a/libraries/microsoft-agents-hosting-core/microsoft_agents/hosting/core/authorization/_log_config.py b/libraries/microsoft-agents-hosting-core/microsoft_agents/hosting/core/authorization/_log_config.py
new file mode 100644
index 00000000..9f953ea3
--- /dev/null
+++ b/libraries/microsoft-agents-hosting-core/microsoft_agents/hosting/core/authorization/_log_config.py
@@ -0,0 +1,172 @@
+# Copyright (c) Microsoft Corporation. All rights reserved.
+# Licensed under the MIT License.
+
+import json
+
+from logging import Logger
+from urllib.parse import urlparse
+
+from microsoft_agents.activity._model_utils import pick_model_dict, SkipNone
+
+from .agent_auth_configuration import AgentAuthConfiguration
+
+_REDACTION_PEEK_LENGTH = 3
+_REDACTION_THRESH = _REDACTION_PEEK_LENGTH + 6
+
+
+def _redact_str(s: str, peek: bool = False) -> str:
+ """Redact a string for logging purposes.
+
+ :arg s: The string to redact.
+ :type s: str
+ :arg peek: Whether to show a peek of the string. Defaults to False.
+ :type peek: bool
+ :return: The redacted string.
+ """
+ if peek and len(s) > _REDACTION_THRESH:
+ return f"{s[:_REDACTION_PEEK_LENGTH]}..."
+ else:
+ return "..."
+
+
+def _redact_str_or_none(s: str | None, peek: bool = False) -> str | None:
+ """Redact a string or None for logging purposes.
+
+ :arg s: The string to redact or None.
+ :type s: str | None
+ :arg peek: Whether to show a peek of the string. Defaults to False.
+ :type peek: bool
+ :return: The redacted string or None.
+ :rtype: str | None
+ """
+ if s is None:
+ return None
+ return _redact_str(s, peek=peek)
+
+
+def _redact_scopes(scopes: list[str] | None) -> str | None:
+ """Redact a list of scopes for logging purposes.
+
+ :arg scopes: The list of scopes to redact.
+ :type scopes: list[str] | None
+ :return: A string summarizing the scopes.
+ :rtype: str | None
+ """
+ if scopes is None:
+ return None
+ return f"... [{len(scopes)} scope(s)]"
+
+
+def _redact_url(url: str) -> str:
+ """
+ Redact a URL for logging purposes.
+
+ :arg url: The URL to redact.
+ :type url: str
+ :return: The redacted URL.
+ :rtype: str
+ """
+ url = url.strip()
+ if not url:
+ return ""
+
+ try:
+ url_parsed = urlparse(url)
+ return f"{url_parsed.scheme}://{url_parsed.netloc}/..."
+ except Exception:
+ return "..."
+
+
+def _redact_url_or_none(url: str | None) -> str | None:
+ """Redact a URL or None for logging purposes.
+
+ :arg url: The URL to redact or None.
+ :type url: str | None
+ :return: The redacted URL or None.
+ :rtype: str | None
+ """
+ if url is None:
+ return None
+ return _redact_url(url)
+
+
+def _summarize_auth_configs(config_map: dict[str, AgentAuthConfiguration]) -> str:
+ """
+ Summarize the authentication configuration for logging.
+
+ :arg config_map: A dictionary of connection configurations.
+ :type config_map: dict[str, :class:`microsoft_agents.hosting.core.AgentAuthConfiguration`]
+ :return: A string summarizing the authentication configuration.
+ :rtype: str
+ """
+ summary = []
+ for connection_name, config in config_map.items():
+ summary.append(
+ pick_model_dict(
+ CONNECTION=connection_name,
+ CONNECTION_NAME=SkipNone(config.CONNECTION_NAME),
+ CLIENTID=SkipNone(_redact_str_or_none(config.CLIENT_ID, peek=True)),
+ TENANTID=SkipNone(_redact_str_or_none(config.TENANT_ID, peek=True)),
+ CLIENTSECRET=SkipNone(_redact_str_or_none(config.CLIENT_SECRET)),
+ AUTHORITY=SkipNone(_redact_url_or_none(config.AUTHORITY)),
+ SCOPES=SkipNone(_redact_scopes(config.SCOPES)),
+ FEDERATED_CLIENT_ID=SkipNone(
+ _redact_str_or_none(config.FEDERATED_CLIENT_ID, peek=True)
+ ),
+ CERT_PFX_FILE=SkipNone(_redact_str_or_none(config.CERT_PFX_FILE)),
+ ALT_BLUEPRINT_ID=SkipNone(
+ _redact_str_or_none(config.ALT_BLUEPRINT_ID, peek=True)
+ ),
+ IDPM_RESOURCE=SkipNone(_redact_url_or_none(config.IDPM_RESOURCE)),
+ AZURE_REGION=SkipNone(_redact_url_or_none(config.AZURE_REGION)),
+ ANONYMOUS_ALLOWED=str(config.ANONYMOUS_ALLOWED),
+ )
+ )
+ return json.dumps(summary, indent=2)
+
+
+def _summarize_connections_map(connections_map: list[dict[str, str]]) -> str:
+ connections_map_output = []
+ for mapping in connections_map:
+ obj = {
+ "CONNECTION": mapping.get("CONNECTION", ""),
+ }
+
+ if "AUDIENCE" in mapping:
+ obj["AUDIENCE"] = mapping["AUDIENCE"]
+
+ if "SERVICEURL" in mapping:
+ service_url = mapping.get("SERVICEURL", "").strip()
+ if service_url != "*":
+ service_url = _redact_url(service_url)
+ obj["SERVICEURL"] = service_url
+
+ connections_map_output.append(obj)
+
+ return json.dumps(connections_map_output, indent=2)
+
+
+def _log_config(
+ logger: Logger,
+ config_map: dict[str, AgentAuthConfiguration],
+ connections_map: list[dict[str, str]],
+) -> None:
+ """
+ Log the configuration of the MSAL connection manager.
+
+ :arg logger: The logger to use for logging.
+ :type logger: :class:`logging.Logger`
+ :arg connections_map: A list of connection mappings.
+ :type connections_map: list[dict[str, str]]
+ :arg config_map: A dictionary of connection configurations.
+ :type config_map: dict[str, :class:`microsoft_agents.hosting.core.AgentAuthConfiguration`]
+ """
+
+ connections_output = _summarize_auth_configs(config_map)
+ connections_map_output = _summarize_connections_map(connections_map)
+
+ logger.info(
+ "\nConnections:\n%s\n\nConnections Map:\n%s",
+ connections_output,
+ connections_map_output,
+ )
diff --git a/libraries/microsoft-agents-hosting-core/microsoft_agents/hosting/core/authorization/connection_manager.py b/libraries/microsoft-agents-hosting-core/microsoft_agents/hosting/core/authorization/connection_manager.py
index a1ac1b88..3efb71fc 100644
--- a/libraries/microsoft-agents-hosting-core/microsoft_agents/hosting/core/authorization/connection_manager.py
+++ b/libraries/microsoft-agents-hosting-core/microsoft_agents/hosting/core/authorization/connection_manager.py
@@ -2,6 +2,8 @@
# Licensed under the MIT License.
import re
+import logging
+
from collections.abc import Callable
from .agent_auth_configuration import AgentAuthConfiguration
@@ -9,6 +11,10 @@
from .claims_identity import ClaimsIdentity
from .connections import Connections
+from ._log_config import _log_config
+
+logger = logging.getLogger(__name__)
+
class ConnectionManager(Connections):
"""
@@ -59,6 +65,7 @@ def __init__(
if connections_map is not None
else kwargs.get("CONNECTIONSMAP", [])
)
+
if isinstance(raw_connections_map, dict):
raw_connections_map = list(raw_connections_map.values())
self._connections_map = raw_connections_map or []
@@ -89,6 +96,8 @@ def __init__(
if not self._connections.get("SERVICE_CONNECTION", None):
raise ValueError("No service connection configuration provided.")
+ _log_config(logger, self._config_map, self._connections_map)
+
def get_connection(self, connection_name: str | None) -> AccessTokenProviderBase:
"""
Get the OAuth connection for the agent.
diff --git a/libraries/microsoft-agents-hosting-core/readme.md b/libraries/microsoft-agents-hosting-core/readme.md
index 3078b97d..04109423 100644
--- a/libraries/microsoft-agents-hosting-core/readme.md
+++ b/libraries/microsoft-agents-hosting-core/readme.md
@@ -16,6 +16,15 @@ This library is part of the **Microsoft 365 Agents SDK for Python** - a comprehe
Date |
Release Notes |
+
+ | 1.2.0 |
+ 2026-07-17 |
+
+
+ 1.2.0 Release Notes
+
+ |
+
| 1.1.0 |
2026-06-19 |
@@ -34,6 +43,15 @@ This library is part of the **Microsoft 365 Agents SDK for Python** - a comprehe
+
+ | 0.9.1 |
+ 2026-05-04 |
+
+
+ 0.9.1 Release Notes
+
+ |
+
| 0.9.0 |
2026-04-15 |
diff --git a/libraries/microsoft-agents-hosting-dialogs/readme.md b/libraries/microsoft-agents-hosting-dialogs/readme.md
index 98630500..a84d4a39 100644
--- a/libraries/microsoft-agents-hosting-dialogs/readme.md
+++ b/libraries/microsoft-agents-hosting-dialogs/readme.md
@@ -64,6 +64,15 @@ dialogs.add(WaterfallDialog("main", [step1, step2]))
Date |
Release Notes |
+
+ | 1.2.0 |
+ 2026-07-17 |
+
+
+ 1.2.0 Release Notes
+
+ |
+
| 1.1.0 |
2026-06-19 |
diff --git a/libraries/microsoft-agents-hosting-fastapi/readme.md b/libraries/microsoft-agents-hosting-fastapi/readme.md
index 7de12b5d..0104ccee 100644
--- a/libraries/microsoft-agents-hosting-fastapi/readme.md
+++ b/libraries/microsoft-agents-hosting-fastapi/readme.md
@@ -9,6 +9,15 @@ This library provides FastAPI integration for Microsoft Agents, enabling you to
Date |
Release Notes |
+
+ | 1.2.0 |
+ 2026-07-17 |
+
+
+ 1.2.0 Release Notes
+
+ |
+
| 1.1.0 |
2026-06-19 |
@@ -27,6 +36,15 @@ This library provides FastAPI integration for Microsoft Agents, enabling you to
+
+ | 0.9.1 |
+ 2026-05-04 |
+
+
+ 0.9.1 Release Notes
+
+ |
+
| 0.9.0 |
2026-04-15 |
diff --git a/libraries/microsoft-agents-hosting-msteams/readme.md b/libraries/microsoft-agents-hosting-msteams/readme.md
index f4b2dfb0..d02a2020 100644
--- a/libraries/microsoft-agents-hosting-msteams/readme.md
+++ b/libraries/microsoft-agents-hosting-msteams/readme.md
@@ -18,6 +18,15 @@ This library is part of the **Microsoft 365 Agents SDK for Python** — a compre
Date |
Release Notes |
+
+ | 1.2.0 |
+ 2026-07-17 |
+
+
+ 1.2.0 Release Notes
+
+ |
+
| 1.1.0 |
2026-06-19 |
@@ -36,6 +45,15 @@ This library is part of the **Microsoft 365 Agents SDK for Python** — a compre
+
+ | 0.9.1 |
+ 2026-05-04 |
+
+
+ 0.9.1 Release Notes
+
+ |
+
| 0.9.0 |
2026-04-15 |
diff --git a/libraries/microsoft-agents-hosting-slack/readme.md b/libraries/microsoft-agents-hosting-slack/readme.md
index d514a393..7b225939 100644
--- a/libraries/microsoft-agents-hosting-slack/readme.md
+++ b/libraries/microsoft-agents-hosting-slack/readme.md
@@ -11,6 +11,15 @@ Integration library for building Slack agents using the Microsoft 365 Agents SDK
Date |
Release Notes |
+
+ | 1.2.0 |
+ 2026-07-17 |
+
+
+ 1.2.0 Release Notes
+
+ |
+
| 1.1.0 |
2026-06-19 |
diff --git a/libraries/microsoft-agents-storage-blob/readme.md b/libraries/microsoft-agents-storage-blob/readme.md
index 2dab87a8..f5721033 100644
--- a/libraries/microsoft-agents-storage-blob/readme.md
+++ b/libraries/microsoft-agents-storage-blob/readme.md
@@ -16,6 +16,15 @@ This library is part of the **Microsoft 365 Agents SDK for Python** - a comprehe
Date |
Release Notes |
+
+ | 1.2.0 |
+ 2026-07-17 |
+
+
+ 1.2.0 Release Notes
+
+ |
+
| 1.1.0 |
2026-06-19 |
@@ -34,6 +43,15 @@ This library is part of the **Microsoft 365 Agents SDK for Python** - a comprehe
+
+ | 0.9.1 |
+ 2026-05-04 |
+
+
+ 0.9.1 Release Notes
+
+ |
+
| 0.9.0 |
2026-04-15 |
diff --git a/libraries/microsoft-agents-storage-cosmos/readme.md b/libraries/microsoft-agents-storage-cosmos/readme.md
index 0e59223c..17081716 100644
--- a/libraries/microsoft-agents-storage-cosmos/readme.md
+++ b/libraries/microsoft-agents-storage-cosmos/readme.md
@@ -16,6 +16,15 @@ This library is part of the **Microsoft 365 Agents SDK for Python** - a comprehe
Date |
Release Notes |
+
+ | 1.2.0 |
+ 2026-07-17 |
+
+
+ 1.2.0 Release Notes
+
+ |
+
| 1.1.0 |
2026-06-19 |
@@ -34,6 +43,15 @@ This library is part of the **Microsoft 365 Agents SDK for Python** - a comprehe
+
+ | 0.9.1 |
+ 2026-05-04 |
+
+
+ 0.9.1 Release Notes
+
+ |
+
| 0.9.0 |
2026-04-15 |
diff --git a/tests/hosting_core/test_log_config.py b/tests/hosting_core/test_log_config.py
new file mode 100644
index 00000000..669945c9
--- /dev/null
+++ b/tests/hosting_core/test_log_config.py
@@ -0,0 +1,146 @@
+import json
+from unittest.mock import Mock
+
+import pytest
+
+from microsoft_agents.hosting.core import AgentAuthConfiguration
+from microsoft_agents.hosting.core.authorization._log_config import (
+ _log_config,
+ _redact_scopes,
+ _redact_str,
+ _redact_str_or_none,
+ _redact_url,
+ _redact_url_or_none,
+ _summarize_auth_configs,
+ _summarize_connections_map,
+)
+
+
+class TestRedactionUtils:
+ @pytest.mark.parametrize("value", ["", "short", "12345678"])
+ def test_redact_str_without_peek(self, value):
+ assert _redact_str(value) == "..."
+
+ def test_redact_str_with_peek_for_long_value(self):
+ assert _redact_str("client-id-secret", peek=True) == "cli..."
+
+ @pytest.mark.parametrize("value", ["", "short", "12345678"])
+ def test_redact_str_with_peek_for_short_values(self, value):
+ assert _redact_str(value, peek=True) == "..."
+
+ def test_redact_str_or_none_returns_none_for_none(self):
+ assert _redact_str_or_none(None) is None
+
+ def test_redact_str_or_none_redacts_value(self):
+ assert _redact_str_or_none("tenant-id-secret", peek=True) == "ten..."
+
+ def test_redact_scopes_returns_none_for_none(self):
+ assert _redact_scopes(None) is None
+
+ @pytest.mark.parametrize(
+ "scopes, expected",
+ [
+ ([], "... [0 scope(s)]"),
+ (["scope1"], "... [1 scope(s)]"),
+ (["scope1", "scope2"], "... [2 scope(s)]"),
+ ],
+ )
+ def test_redact_scopes_summarizes_count(self, scopes, expected):
+ assert _redact_scopes(scopes) == expected
+
+ @pytest.mark.parametrize(
+ "url, expected",
+ [
+ (
+ "https://login.microsoftonline.com/tenant/oauth2/v2.0/token",
+ "https://login.microsoftonline.com/...",
+ ),
+ (" https://example.com/path?secret=value ", "https://example.com/..."),
+ ("", ""),
+ (" ", ""),
+ ],
+ )
+ def test_redact_url(self, url, expected):
+ assert _redact_url(url) == expected
+
+ def test_redact_url_or_none_returns_none_for_none(self):
+ assert _redact_url_or_none(None) is None
+
+ def test_redact_url_or_none_redacts_value(self):
+ assert (
+ _redact_url_or_none("https://example.com/path") == "https://example.com/..."
+ )
+
+
+class TestLogConfig:
+ def test_summarize_auth_configs_formats_connections_as_json_array(self):
+ summary = _summarize_auth_configs(
+ {
+ "SERVICE_CONNECTION": AgentAuthConfiguration(
+ client_id="client-id-secret",
+ tenant_id="tenant-id-secret",
+ client_secret="client-secret",
+ connection_name="configured-name",
+ authority="https://login.microsoftonline.com/tenant/oauth2/v2.0/token",
+ scopes=["scope1", "scope2"],
+ )
+ }
+ )
+
+ parsed_summary = json.loads(summary)
+
+ assert parsed_summary == [
+ {
+ "CONNECTION": "SERVICE_CONNECTION",
+ "CONNECTION_NAME": "configured-name",
+ "CLIENTID": "cli...",
+ "TENANTID": "ten...",
+ "CLIENTSECRET": "...",
+ "AUTHORITY": "https://login.microsoftonline.com/...",
+ "SCOPES": "... [2 scope(s)]",
+ "ANONYMOUS_ALLOWED": "False",
+ }
+ ]
+ assert "client-secret" not in summary
+ assert "oauth2/v2.0/token" not in summary
+
+ def test_summarize_connections_map_redacts_service_urls(self):
+ summary = _summarize_connections_map(
+ [
+ {
+ "CONNECTION": "SERVICE_CONNECTION",
+ "AUDIENCE": "api://service",
+ "SERVICEURL": "https://service.example.com/path?secret=value",
+ },
+ {"CONNECTION": "AGENTIC", "SERVICEURL": "*"},
+ ]
+ )
+
+ parsed_summary = json.loads(summary)
+
+ assert parsed_summary == [
+ {
+ "CONNECTION": "SERVICE_CONNECTION",
+ "AUDIENCE": "api://service",
+ "SERVICEURL": "https://service.example.com/...",
+ },
+ {"CONNECTION": "AGENTIC", "SERVICEURL": "*"},
+ ]
+ assert "path?secret=value" not in summary
+
+ def test_log_config_uses_clean_parameterized_format(self):
+ logger = Mock()
+ config_map = {
+ "SERVICE_CONNECTION": AgentAuthConfiguration(client_id="client-id-secret")
+ }
+ connections_map = [{"CONNECTION": "SERVICE_CONNECTION", "SERVICEURL": "*"}]
+
+ _log_config(logger, config_map, connections_map)
+
+ logger.info.assert_called_once()
+ message, connections_output, connections_map_output = logger.info.call_args.args
+ assert message == "\nConnections:\n%s\n\nConnections Map:\n%s"
+ assert json.loads(connections_output)[0]["CONNECTION"] == "SERVICE_CONNECTION"
+ assert json.loads(connections_map_output)[0]["CONNECTION"] == (
+ "SERVICE_CONNECTION"
+ )