feat: add azure-ai-agentserver-activity package (Activity Protocol)#47489
Conversation
Initial release of the Activity Protocol host package for Azure AI Foundry hosted agents. Provides Starlette-based HTTP endpoints for Activity Protocol traffic with M365 Agents SDK integration. Package (azure-ai-agentserver-activity 1.0.0b1): - ActivityAgentServerHost with POST /activity/messages and /api/messages - Decorator API: @app.activity(type), @app.error for zero-config usage - Custom handler support for full M365 SDK control - Auto-initialization of M365 SDK from environment variables - MSAL auth patches for Foundry container MAIB auth (apply_msal_patches) - Session ID resolution (query param, header, config, UUID fallback) - Activity/session ID sanitization for header injection defense - OpenTelemetry distributed tracing and W3C Baggage propagation - Error-source classification (x-platform-error-source) Samples: - simple_activity_agent (echo bot) - streaming_activity_agent (Azure OpenAI streaming) - cards_activity_agent (Adaptive/Hero/Thumbnail/Receipt cards) - auto_signin_activity_agent (OAuth with Graph/GitHub) - semantic_kernel_activity_agent (SK agent with tools, multi-turn) - suggested_actions_activity_agent (quick-reply buttons) Tests: 26 tests covering routes, session resolution, error classification, tracing, decorator pattern, and ID sanitization edge cases.
There was a problem hiding this comment.
Pull request overview
This PR introduces a new preview package, azure-ai-agentserver-activity (Activity Protocol host), adding Starlette endpoints intended for Azure AI Foundry hosted agents and integrating with the Microsoft 365 Agents SDK (decorator mode + custom-handler mode). It also wires the new package into the agentserver CI/test targeting so it is built and validated with the rest of the service directory.
Changes:
- Added
azure-ai-agentserver-activitypackage withActivityAgentServerHost, M365 bridge + MSAL patches, ID sanitization, tracing/baggage, and error-source classification. - Added unit tests and multiple runnable samples (simple, streaming, cards, suggested actions, semantic-kernel, auto sign-in).
- Updated
sdk/agentserver/ci.ymlandsdk/agentserver/tests.ymlto include the new package in artifact/test targeting.
Reviewed changes
Copilot reviewed 39 out of 40 changed files in this pull request and generated 6 comments.
Show a summary per file
| File | Description |
|---|---|
| sdk/agentserver/tests.yml | Adds activity package to test targeting string. |
| sdk/agentserver/ci.yml | Adds activity package to pipeline artifacts. |
| sdk/agentserver/azure-ai-agentserver-activity/tests/test_tracing.py | Adds tracing + baggage propagation tests. |
| sdk/agentserver/azure-ai-agentserver-activity/tests/test_session_headers.py | Adds session resolution/response header tests. |
| sdk/agentserver/azure-ai-agentserver-activity/tests/test_server_routes.py | Adds route/endpoint behavior tests. |
| sdk/agentserver/azure-ai-agentserver-activity/tests/test_sanitize_id.py | Adds _sanitize_id edge-case tests. |
| sdk/agentserver/azure-ai-agentserver-activity/tests/test_id_sanitization.py | Adds end-to-end activity-id sanitization tests. |
| sdk/agentserver/azure-ai-agentserver-activity/tests/test_error_source_classification.py | Adds tests for x-platform-error-source classification. |
| sdk/agentserver/azure-ai-agentserver-activity/tests/test_decorator_pattern.py | Adds decorator wiring tests for bridge handler. |
| sdk/agentserver/azure-ai-agentserver-activity/tests/conftest.py | Adds shared fixtures + marker declaration. |
| sdk/agentserver/azure-ai-agentserver-activity/samples/README.md | Documents available samples and usage patterns. |
| sdk/agentserver/azure-ai-agentserver-activity/samples/simple_activity_agent/simple_activity_agent.py | Simple decorator-based echo agent sample. |
| sdk/agentserver/azure-ai-agentserver-activity/samples/simple_activity_agent/requirements.txt | Sample dependencies. |
| sdk/agentserver/azure-ai-agentserver-activity/samples/streaming_activity_agent/streaming_activity_agent.py | Streaming Azure OpenAI sample using streaming_response. |
| sdk/agentserver/azure-ai-agentserver-activity/samples/streaming_activity_agent/requirements.txt | Sample dependencies. |
| sdk/agentserver/azure-ai-agentserver-activity/samples/cards_activity_agent/cards_activity_agent.py | Rich cards sample (adaptive/hero/thumbnail/receipt). |
| sdk/agentserver/azure-ai-agentserver-activity/samples/cards_activity_agent/requirements.txt | Sample dependencies. |
| sdk/agentserver/azure-ai-agentserver-activity/samples/suggested_actions_activity_agent/suggested_actions_activity_agent.py | Suggested actions / quick-reply buttons sample. |
| sdk/agentserver/azure-ai-agentserver-activity/samples/suggested_actions_activity_agent/requirements.txt | Sample dependencies. |
| sdk/agentserver/azure-ai-agentserver-activity/samples/semantic_kernel_activity_agent/semantic_kernel_activity_agent.py | Semantic Kernel multi-turn + streaming sample. |
| sdk/agentserver/azure-ai-agentserver-activity/samples/semantic_kernel_activity_agent/requirements.txt | Sample dependencies. |
| sdk/agentserver/azure-ai-agentserver-activity/samples/auto_signin_activity_agent/auto_signin_activity_agent.py | OAuth auto sign-in sample using handler pattern + MSAL patches. |
| sdk/agentserver/azure-ai-agentserver-activity/samples/auto_signin_activity_agent/requirements.txt | Sample dependencies. |
| sdk/agentserver/azure-ai-agentserver-activity/README.md | Package README describing host usage and API surface. |
| sdk/agentserver/azure-ai-agentserver-activity/pyrightconfig.json | Pyright config excluding samples. |
| sdk/agentserver/azure-ai-agentserver-activity/pyproject.toml | Package metadata, deps, and Azure SDK build config. |
| sdk/agentserver/azure-ai-agentserver-activity/MANIFEST.in | sdist packaging manifest. |
| sdk/agentserver/azure-ai-agentserver-activity/LICENSE | MIT license file. |
| sdk/agentserver/azure-ai-agentserver-activity/dev_requirements.txt | Dev/test requirements. |
| sdk/agentserver/azure-ai-agentserver-activity/cspell.json | Spellchecker configuration. |
| sdk/agentserver/azure-ai-agentserver-activity/CHANGELOG.md | Initial preview release changelog. |
| sdk/agentserver/azure-ai-agentserver-activity/azure/ai/agentserver/activity/py.typed | Marks package as typed. |
| sdk/agentserver/azure-ai-agentserver-activity/azure/ai/agentserver/activity/_version.py | Defines package version. |
| sdk/agentserver/azure-ai-agentserver-activity/azure/ai/agentserver/activity/_m365_bridge.py | Implements M365 SDK lazy-init + bridge handler + MSAL patching. |
| sdk/agentserver/azure-ai-agentserver-activity/azure/ai/agentserver/activity/_constants.py | Defines Activity protocol constants and header names. |
| sdk/agentserver/azure-ai-agentserver-activity/azure/ai/agentserver/activity/_activity.py | Implements ActivityAgentServerHost endpoint, sanitization, tracing, and error handling. |
| sdk/agentserver/azure-ai-agentserver-activity/azure/ai/agentserver/activity/init.py | Exposes public API (ActivityAgentServerHost, apply_msal_patches). |
| sdk/agentserver/azure-ai-agentserver-activity/azure/ai/agentserver/init.py | Namespace package init. |
| sdk/agentserver/azure-ai-agentserver-activity/azure/ai/init.py | Namespace package init. |
| sdk/agentserver/azure-ai-agentserver-activity/azure/init.py | Namespace package init. |
…al pattern - Remove AGENTAPPLICATION fallback defaults from _initialize_default_env_vars (STARTTYPINGTIMER, REMOVERECIPIENTMENTION, NORMALIZEMENTIONS, USERAUTHORIZATION__AUTOSIGNIN) - Derive CONNECTIONS__ vars from Foundry-native env vars (FOUNDRY_AGENT_BLUEPRINT_CLIENT_ID, FOUNDRY_AGENT_TENANT_ID) using helper functions _get_nonempty / _set_if_missing - Replace old archive-style samples (auto_signin, cards, semantic_kernel, simple, streaming, suggested_actions) with minimal invocations-pattern samples: - echo-agent: Tier 1 zero-config, auto-init from env vars - multi-protocol-agent: Tier 2 Activity + Invocations on one host - self-hosted-agent: Tier 3 full M365 SDK control
…ging - Add _BaggageSpanProcessor (subclass of SDK SpanProcessor) to promote all W3C baggage entries onto every child span - Replace single-logger filter with global setLogRecordFactory so SessionId/UserIsolationKey/ChatIsolationKey/Protocol appear on all loggers' records - Resolve correlation ids and bind contextvars at the top of the endpoint so boundary logs are populated - Add structured request/response logging plus activity.request/activity.response/activity.error span events with full values
…cing The Foundry gateway already emits the turn-level request span and core's TraceContextMiddleware + _FoundryEnrichmentSpanProcessor propagate trace context and stamp identity/session/conversation/project onto every child span. The activity package now only sets baggage (its source-of-truth correlation keys) and emits structured request/response/error logs, avoiding a redundant wrapper span and duplicate/conflicting gen_ai.* + microsoft.foundry.project.id attributes. Verified live (groot-v2 v6): no handle_activity spans, child spans still carry all correlation keys + identity, reminder/proactive flow fully traced, zero errors.
- Add aiohttp constraint to core package (>=3.10.0,<4.0.0a1) to prevent pre-release 4.0.0a1 which fails to compile on Python 3.11 due to missing longintrepr.h - Import Token from opentelemetry.context for proper type hinting - Type baggage_token as Optional[Token[Any]] instead of Optional[object] - Add explicit Response type annotation for handler response with type: ignore for JSONResponse variance
- Remove unused JSONResponse import - Fix opentelemetry import grouping (separate from starlette) - Add missing docstring parameters: 'record' to _enrich_record - Add complete docstrings with param/return types for: - _sanitize_id: document value param and return - _classify_error: document exc param and return tuple - activity() decorator: add return type - error() decorator: add return type and fn param type - _response_body_preview: document response, limit, and return - _create_activity_endpoint: document request param and return
… (b4/b1)" This reverts commit bfe978e.
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 52 out of 53 changed files in this pull request and generated 1 comment.
Comments suppressed due to low confidence (5)
sdk/agentserver/azure-ai-agentserver-activity/azure/ai/agentserver/activity/_m365_bridge.py:271
- The bridge creates a trusted
ClaimsIdentitywithout validating the inboundAuthorizationheader.HttpAdapterBase.process_requesttreatsget_claims_identity()as the identity already attached by authentication middleware, so missing or arbitrary bearer tokens reach the agent as authenticated requests. Validate the JWT with the M365 Starlette middleware (or equivalent) and pass only its validated claims; outbound credential selection must not replace the inbound caller identity.
# Synthesize the outbound claims for this turn and attach them to the
# request so the framework adapter reads them via get_claims_identity
# (mirrors how the SDK adapters read claims set by auth middleware).
request.state.claims_identity = _build_outbound_claims(
ClaimsIdentity,
digital_worker=digital_worker,
is_hosted=is_hosted,
bot_app_id=bot_app_id,
)
sdk/agentserver/azure-ai-agentserver-activity/samples/04-custom-handler/main.py:63
- This sends a managed-identity bearer token to a URL derived directly from the untrusted activity
serviceUrl. The warning does not mitigate the executable SSRF/token-exfiltration path: anyone who can call this sample can choose the token recipient. Remove the credentialed outbound call or authenticate the activity and enforce a strict Bot Connector endpoint allowlist before minting or attaching the token.
url = f"{service_url.rstrip('/')}/v3/conversations/{conversation_id}/activities"
payload = {"type": "message", "text": text}
headers = {"Authorization": f"Bearer {token}", "Content-Type": "application/json"}
async with aiohttp.ClientSession() as session:
async with session.post(url, json=payload, headers=headers) as resp:
sdk/agentserver/azure-ai-agentserver-activity/azure/ai/agentserver/activity/_activity.py:138
- This global record factory labels every subsequent log record as
activity, regardless of which route is executing. In the includedMultiProtocolHost, invocation-handler logs will therefore be exported withProtocol=activity. Track the active protocol in request-scoped context and set/reset it in the activity endpoint instead of stamping a process-wide constant.
if not hasattr(record, LogRecordFields.PROTOCOL):
setattr(record, LogRecordFields.PROTOCOL, ActivityConstants.PROTOCOL)
sdk/agentserver/azure-ai-agentserver-activity/azure/ai/agentserver/activity/_activity.py:750
- All handler-returned errors default to
upstream, including the M365 adapter's 400 response for a missing activity type orconversation.id. Those are malformed caller payloads and should be classified asuser; otherwise platform routing attributes client errors to the agent. Preserve an origin-specific classification from the adapter (or classify its validation responses before this fallback).
# The package contract guarantees an error-source on every error
# response. A handler (or the M365 adapter) can return a 4xx/5xx
# directly without raising, which skips the exception path below, so
# classify any unclassified error status as ``upstream`` here.
status_code = getattr(response, "status_code", 0)
if status_code >= 400 and ERROR_SOURCE not in response.headers:
response.headers[ERROR_SOURCE] = ErrorSource.UPSTREAM
sdk/agentserver/azure-ai-agentserver-activity/README.md:124
- The PR description advertises streaming, cards, auto-sign-in, Semantic Kernel, and suggested-actions samples, but the package's sample set contains only echo, component override, self-hosted, custom-handler, and multi-protocol scenarios. Either add the advertised scenarios or update the PR description so reviewers and users are not promised absent examples.
See the [samples directory](https://github.com/Azure/azure-sdk-for-python/tree/main/sdk/agentserver/azure-ai-agentserver-activity/samples) for runnable scenarios, ordered as a learning path:
- `01-echo` — the simplest agent: the host builds the M365 stack and you register handlers directly on it to echo the user's message back.
- `02-custom-components` — override one or more M365 components (storage, connection manager, adapter, authorization, config) while the host builds the rest.
- `03-self-hosted-app` — build the M365 `AgentApplication` yourself and host it by passing `agent_app=`.
- `04-custom-handler` — own the request pipeline with `request_handler=` (the M365 SDK is not initialized).
- `05-multi-protocol` — compose the Activity and Invocations protocols on a single server.
This comment has been minimized.
This comment has been minimized.
f0c8ccc to
754b594
Compare
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 51 out of 52 changed files in this pull request and generated no new comments.
Comments suppressed due to low confidence (5)
sdk/agentserver/azure-ai-agentserver-activity/azure/ai/agentserver/activity/_m365_bridge.py:270
- The bridge never validates the inbound
Authorizationheader; instead it creates a trustedClaimsIdentityfor every request.HttpAdapterBase.process_requesttrustsget_claims_identity()(the standard M365 hosts populate it only after JWT validation), so requests with a missing or invalid token can invoke the agent and trigger authenticated outbound replies. Validate the inbound bearer token before assigning claims, and only construct the outbound identity after authentication succeeds.
request.state.claims_identity = _build_outbound_claims(
ClaimsIdentity,
digital_worker=digital_worker,
is_hosted=is_hosted,
bot_app_id=bot_app_id,
sdk/agentserver/azure-ai-agentserver-activity/samples/04-custom-handler/main.py:60
- This runnable sample sends a managed-identity bearer token to an attacker-controlled
serviceUrl. The warning does not prevent exploitation: any caller that reaches the unauthenticated custom-handler endpoint can supply its own URL and receive the Bot Connector token or induce SSRF. Authenticate the activity and enforce a trusted Connector endpoint allowlist before minting or attaching credentials; otherwise remove this outbound call from the sample.
url = f"{service_url.rstrip('/')}/v3/conversations/{conversation_id}/activities"
payload = {"type": "message", "text": text}
headers = {"Authorization": f"Bearer {token}", "Content-Type": "application/json"}
sdk/agentserver/azure-ai-agentserver-activity/azure/ai/agentserver/activity/_activity.py:530
- The zero-configuration hosted path also reaches this fallback, so every hosted instance uses process-local
MemoryStorage. M365TurnStateand OAuth state will be lost on restart and diverge across replicas, making multi-turn hosted agents unreliable. Select durable Foundry storage whenself.config.is_hosted, or fail fast and require callers to provide a durablestoragebackend in hosted environments.
# TODO: use a durable hosted store (FoundryStorage) when running in a
# Foundry-hosted container; MemoryStorage is the local-testing default.
# pylint: disable=import-error,no-name-in-module
from microsoft_agents.hosting.core import MemoryStorage
return MemoryStorage()
sdk/agentserver/azure-ai-agentserver-activity/azure/ai/agentserver/activity/_cloud_adapter.py:80
- Only the top-level JSON-object check runs before this call. The M365
HttpAdapterBase.process_requestperformsActivity.model_validate(body)without catching validation errors, so malformed nested activity data (for example, a stringconversation) escapes here and is converted by_run_handlerinto a 500/upstream response instead of a 400/user response. Catch the model-validation failure or validate the Activity before delegation and return the invalid-request envelope.
# Process using the shared base implementation
http_response = await self._adapter.process_request(adapted_request, agent)
sdk/agentserver/azure-ai-agentserver-activity/azure/ai/agentserver/activity/_activity.py:750
- This fallback misclassifies M365 adapter validation responses.
HttpAdapterBasereturns 400 for a malformed Activity and 401 for authorization failures;_to_starlette_responsedoes not add an error source, so both are stampedupstreamhere even though they are caller/user errors. Preserve an adapter classification or classify these known M365 4xx responses asuserbefore applying the upstream fallback.
if status_code >= 400 and ERROR_SOURCE not in response.headers:
response.headers[ERROR_SOURCE] = ErrorSource.UPSTREAM
This comment has been minimized.
This comment has been minimized.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 2ee2e202-5f6a-4262-896b-4dae76544d0d
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 52 out of 53 changed files in this pull request and generated 2 comments.
Comments suppressed due to low confidence (4)
sdk/agentserver/azure-ai-agentserver-activity/azure/ai/agentserver/activity/_activity.py:750
StarletteCloudAdapterconverts M365 validation failures to a 400invalid_requestresponse without setting an error source, so this fallback labels themupstream. A malformed activity object (for example, one missing its required type) is a caller error and should be classified asuser; stamp adapter-generated 4xx responses accordingly before this fallback.
status_code = getattr(response, "status_code", 0)
if status_code >= 400 and ERROR_SOURCE not in response.headers:
response.headers[ERROR_SOURCE] = ErrorSource.UPSTREAM
sdk/agentserver/azure-ai-agentserver-activity/samples/04-custom-handler/main.py:60
- This runnable sample sends a managed-identity bearer token to the request-controlled
serviceUrlwithout inbound JWT validation or a trusted-host allowlist. The warning does not prevent deployment; an attacker reaching this endpoint can trigger SSRF and send the token to an arbitrary host. Authenticate the activity and validate the connector endpoint before minting or attaching credentials, or remove the authenticated outbound call.
url = f"{service_url.rstrip('/')}/v3/conversations/{conversation_id}/activities"
payload = {"type": "message", "text": text}
headers = {"Authorization": f"Bearer {token}", "Content-Type": "application/json"}
sdk/agentserver/azure-ai-agentserver-activity/README.md:124
- The PR description promises streaming, cards, auto-sign-in, Semantic Kernel, and suggested-actions samples, but the added sample tree contains only these five different scenarios. Add the advertised samples or update the PR description so reviewers and users are not told functionality is included when it is absent.
- `01-echo` — the simplest agent: the host builds the M365 stack and you register handlers directly on it to echo the user's message back.
- `02-custom-components` — override one or more M365 components (storage, connection manager, adapter, authorization, config) while the host builds the rest.
- `03-self-hosted-app` — build the M365 `AgentApplication` yourself and host it by passing `agent_app=`.
- `04-custom-handler` — own the request pipeline with `request_handler=` (the M365 SDK is not initialized).
- `05-multi-protocol` — compose the Activity and Invocations protocols on a single server.
sdk/agentserver/azure-ai-agentserver-activity/azure/ai/agentserver/activity/_activity.py:530
- The default hosted path unconditionally falls back to
MemoryStorage. Turn state will be lost on restart and diverge across replicas, breaking multi-turn and OAuth flows even though this host targets Foundry containers. Use a durable hosted store by default, or require callers to provide storage whenself.config.is_hostedis true.
# Foundry-hosted container; MemoryStorage is the local-testing default.
# pylint: disable=import-error,no-name-in-module
from microsoft_agents.hosting.core import MemoryStorage
return MemoryStorage()
…bumped apistub pin; API surface unchanged)
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 54 out of 55 changed files in this pull request and generated no new comments.
Comments suppressed due to low confidence (4)
sdk/agentserver/azure-ai-agentserver-activity/azure/ai/agentserver/activity/_m365_bridge.py:270
- The bridge never validates the inbound
Authorizationheader. These claims are synthesized from local configuration, andHttpAdapterBase.process_request()trustsget_claims_identity()without validating a token, so requests with a missing or invalid bearer token can execute the agent as authenticated and trigger credentialed outbound actions. Authenticate the inbound JWT with the M365 token validator/ASGI middleware and pass those validated inbound claims here; outbound credential selection must remain separate.
request.state.claims_identity = _build_outbound_claims(
ClaimsIdentity,
digital_worker=digital_worker,
is_hosted=is_hosted,
bot_app_id=bot_app_id,
sdk/agentserver/azure-ai-agentserver-activity/samples/04-custom-handler/main.py:63
- This sample sends a managed-identity bearer token to
service_url, which is taken directly from an unauthenticated request. An attacker can supply their own URL and exfiltrate the token or use the container for SSRF; the warning above does not make the runnable sample safe. Authenticate the inbound Bot Framework JWT and enforce an HTTPS allowlist before acquiring or attaching credentials, or remove this outbound call from the sample.
url = f"{service_url.rstrip('/')}/v3/conversations/{conversation_id}/activities"
payload = {"type": "message", "text": text}
headers = {"Authorization": f"Bearer {token}", "Content-Type": "application/json"}
async with aiohttp.ClientSession() as session:
async with session.post(url, json=payload, headers=headers) as resp:
sdk/agentserver/azure-ai-agentserver-activity/azure/ai/agentserver/activity/_activity.py:530
- The default constructor uses
MemoryStorageeven in a Foundry-hosted container. Turn state is therefore isolated per replica and lost on restart, which breaks multi-turn state and authorization flows under normal hosted scaling. Keep this fallback only for local execution; hosted mode should resolve durable storage or fail construction unless the caller supplies one.
# TODO: use a durable hosted store (FoundryStorage) when running in a
# Foundry-hosted container; MemoryStorage is the local-testing default.
# pylint: disable=import-error,no-name-in-module
from microsoft_agents.hosting.core import MemoryStorage
return MemoryStorage()
sdk/agentserver/azure-ai-agentserver-activity/README.md:124
- The PR description advertises six streaming/cards/OAuth/Semantic Kernel/suggested-actions samples, but this package lists five different samples and none of those advertised scenarios are present. Either add the promised samples or update the PR description so reviewers and release notes reflect what is actually shipping.
- `01-echo` — the simplest agent: the host builds the M365 stack and you register handlers directly on it to echo the user's message back.
- `02-custom-components` — override one or more M365 components (storage, connection manager, adapter, authorization, config) while the host builds the rest.
- `03-self-hosted-app` — build the M365 `AgentApplication` yourself and host it by passing `agent_app=`.
- `04-custom-handler` — own the request pipeline with `request_handler=` (the M365 SDK is not initialized).
- `05-multi-protocol` — compose the Activity and Invocations protocols on a single server.
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 54 out of 55 changed files in this pull request and generated 1 comment.
Comments suppressed due to low confidence (3)
sdk/agentserver/azure-ai-agentserver-activity/samples/04-custom-handler/main.py:63
- This sample sends a managed-identity Bot Connector bearer token to the caller-controlled
serviceUrl. The warning documents the SSRF/token-exfiltration path but does not prevent it, so copying or running the sample in a hosted container exposes a production credential. Mandatory: authenticate the inbound activity and validate the parsed URL against the supported Bot Connector endpoint allowlist before acquiring or attaching the token.
async with session.post(url, json=payload, headers=headers) as resp:
sdk/agentserver/azure-ai-agentserver-activity/azure/ai/agentserver/activity/_activity.py:615
- Malformed-JSON and non-object requests return through
_build_bad_request, but this response only receives the session ID; unlike successful and handler-error paths, it omits the requiredx-agent-activity-idresponse header. Generate a sanitized activity ID for this early error path as well so the header contract is consistent on every response.
headers=_apply_error_source_headers({}, ErrorSource.USER),
sdk/agentserver/azure-ai-agentserver-activity/README.md:124
- The PR description promises six different samples (streaming, cards, auto-sign-in, Semantic Kernel, suggested actions, and echo), while the package adds these five samples covering echo, components, self-hosting, a custom handler, and multi-protocol composition. Either add the advertised scenarios or update the PR description so reviewers and release notes reflect the shipped sample set.
- `01-echo` — the simplest agent: the host builds the M365 stack and you register handlers directly on it to echo the user's message back.
- `02-custom-components` — override one or more M365 components (storage, connection manager, adapter, authorization, config) while the host builds the rest.
- `03-self-hosted-app` — build the M365 `AgentApplication` yourself and host it by passing `agent_app=`.
- `04-custom-handler` — own the request pipeline with `request_handler=` (the M365 SDK is not initialized).
- `05-multi-protocol` — compose the Activity and Invocations protocols on a single server.
[Pilot] PR Pipeline Failure AnalysisA CI pipeline failed on this pull request. Here is an automated analysis of what went wrong and how to get the build green. What failedThe Mindependency (minimum dependency) validation step failed (build #6593821). The check found dependencies declared in the new packages that are not listed in the repo's
Recommended next steps
Raw pipeline analysis (azsdk ci analyze)
|
…alyze dependencies)
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 55 out of 56 changed files in this pull request and generated 1 comment.
Comments suppressed due to low confidence (5)
sdk/agentserver/azure-ai-agentserver-activity/samples/04-custom-handler/main.py:63
- This sample sends a managed-identity bearer token to the payload-controlled
serviceUrl. The warning does not prevent a runnable sample from enabling SSRF and token exfiltration when copied or deployed. Authenticate the inbound activity and validate the destination against the Bot Connector allowlist before acquiring or attaching the token; otherwise keep this sample credential-free.
url = f"{service_url.rstrip('/')}/v3/conversations/{conversation_id}/activities"
payload = {"type": "message", "text": text}
headers = {"Authorization": f"Bearer {token}", "Content-Type": "application/json"}
async with aiohttp.ClientSession() as session:
async with session.post(url, json=payload, headers=headers) as resp:
sdk/agentserver/azure-ai-agentserver-activity/azure/ai/agentserver/activity/_cloud_adapter.py:80
- Malformed activity objects can become 500/upstream responses here. The M365
HttpAdapterBase.process_requestcallsActivity.model_validate(body)outside its own error handling, so payloads with invalid field types raise a validation exception;_run_handlerthen catches it as an internal handler failure. Convert M365 activity-validation failures to a 400 response classified asuser.
http_response = await self._adapter.process_request(adapted_request, agent)
sdk/agentserver/azure-ai-agentserver-activity/azure/ai/agentserver/activity/_activity.py:750
- This blanket fallback misclassifies M365-generated client errors as
upstream. For example, a JSON object missingtypeorconversation.idis returned byHttpAdapterBaseas 400, reaches this branch without a source header, and is labeled upstream even though the package contract classifies request validation failures asuser. Have the bridge annotate M365 4xx validation/auth responses as user before this generic handler fallback.
# The package contract guarantees an error-source on every error
# response. A handler (or the M365 adapter) can return a 4xx/5xx
# directly without raising, which skips the exception path below, so
# classify any unclassified error status as ``upstream`` here.
status_code = getattr(response, "status_code", 0)
if status_code >= 400 and ERROR_SOURCE not in response.headers:
response.headers[ERROR_SOURCE] = ErrorSource.UPSTREAM
sdk/agentserver/azure-ai-agentserver-activity/azure/ai/agentserver/activity/_activity.py:530
- The default hosted-agent path also reaches this unconditional
MemoryStorage, so turn/OAuth state is lost on restart and split across replicas. That makes the advertised zero-config hosted path unreliable for multi-turn agents. Until a durable Foundry store is available, require callers running withself.config.is_hostedto supplystoragerather than silently selecting a local-only backend.
# TODO: use a durable hosted store (FoundryStorage) when running in a
# Foundry-hosted container; MemoryStorage is the local-testing default.
# pylint: disable=import-error,no-name-in-module
from microsoft_agents.hosting.core import MemoryStorage
return MemoryStorage()
sdk/agentserver/azure-ai-agentserver-activity/README.md:124
- The PR description promises streaming, cards, auto-sign-in, Semantic Kernel, and suggested-actions samples, but this package includes a different five-sample set and none of those scenarios. Either add the described samples or update the PR description so the advertised deliverables match the change.
- `01-echo` — the simplest agent: the host builds the M365 stack and you register handlers directly on it to echo the user's message back.
- `02-custom-components` — override one or more M365 components (storage, connection manager, adapter, authorization, config) while the host builds the rest.
- `03-self-hosted-app` — build the M365 `AgentApplication` yourself and host it by passing `agent_app=`.
- `04-custom-handler` — own the request pipeline with `request_handler=` (the M365 SDK is not initialized).
- `05-multi-protocol` — compose the Activity and Invocations protocols on a single server.
Initial release of the Activity Protocol host package for Azure AI Foundry hosted agents. Provides Starlette-based HTTP endpoints for Activity Protocol traffic with M365 Agents SDK integration.
Package (azure-ai-agentserver-activity 1.0.0b1):
Samples:
Tests: 26 tests covering routes, session resolution, error classification, tracing, decorator pattern, and ID sanitization edge cases.