* Update hosting channel endpoint paths Treat channel paths as concrete endpoint paths so built-in channels can be mounted at their defaults or at the app root without sample-specific subclasses. Update docs, tests, and the Foundry Telegram Invocations sample accordingly. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Add push support to ActivityProtocolChannel Implement the ChannelPush protocol so the Activity Protocol channel can receive cross-channel fan-out (ResponseTarget.all_linked) and echo_input replay as a non-originating destination: - Add push() that reconstructs a proactive Bot Framework activity (bot/user swap) from the stored conversation reference and POSTs it to /v3/conversations/{id}/activities. - Record a ChannelIdentity (service_url, conversation, bot, user, channel_id, locale) on ChannelRequest.identity so the host registers the channel under its isolation key for fan-out resolution. - Route the streaming path through deliver_response so Activity-originated turns broadcast like Telegram/Discord. - Add tests for push delivery, service_url validation, ChannelPush instance check, and inbound identity recording. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Don't delete Telegram webhook on shutdown by default The TelegramChannel deleted its webhook on shutdown in webhook mode. During a rolling redeploy the new revision registers the webhook on startup, then the old revision's shutdown deletes it, silently breaking inbound delivery until the next boot. setWebhook is overwriting/idempotent, so startup re-asserts the webhook every boot and no teardown is needed. Add a delete_webhook_on_shutdown flag (default False) so teardown is opt-in for ephemeral deployments, and leave the webhook in place otherwise. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Fix Activity channel streaming on non-Teams channels (405 on updateActivity) The Activity Protocol channel streamed replies the Teams way: POST a placeholder, then PUT-edit it as tokens arrive. Only Teams supports the updateActivity REST op; Web Chat, Direct Line and the Emulator return 405 Method Not Allowed on the PUT, so the user saw only the placeholder. Gate the placeholder+edit flow on edit-capable channels (msteams). Other channels now buffer the stream and POST a single final message, mirroring the non-streaming path's fan-out and response-hook semantics. Also add a defensive 405 fallback inside the Teams edit loop so an unexpected 405 can never strand the user on the placeholder. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * fix(hosting-activity-protocol): don't parse Teams inline attachment content as a URI Teams message activities include a text/html attachment whose inline `content` is raw HTML (not a URL). _parse_activity fell back to `attachment["content"]` and passed it to Content.from_uri, raising ContentError ("URI must contain a scheme") and failing the whole turn, so Teams users got no response. Only treat `contentUrl` as a URI, require an absolute scheme, and skip unparseable attachments defensively instead of failing the message. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * feat(hosting-activity-protocol): native slash-command dispatch for Teams/Activity Add a commands= parameter to ActivityProtocolChannel that intercepts a leading /command (after stripping the bot's own @mention) and dispatches to ChannelCommand handlers, mirroring the Telegram channel. Unknown commands fall through to the agent. The channel run_hook is applied to command requests so handlers observe the same resolved isolation key as ordinary messages, and handler errors are swallowed (200, no Bot Service retry of non-idempotent commands). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * feat(hosting): silent attributed Telegram echoes + Teams markdown rendering - hosting-telegram: send cross-channel input echoes with disable_notification (silent) and detect echo payloads so they aren't re-broadcast. - hosting-activity-protocol: render outbound + push activities as textFormat 'markdown' so Teams shows formatted replies (enables per-channel variants). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * fix(hosting-activity-protocol): address PR #6307 review feedback Consult the host delivery pipeline even for empty streamed replies so ResponseTarget.none is honoured and non-originating fan-out is consulted instead of always emitting an originating "(no response)" message. Applies to both the progressive-edit (Teams) and buffered (Web Chat/Direct Line) streaming paths. Re-validate service_url against the allow-list in push(): the identity is read from a persisted store and push runs out-of-band, so the captured service_url must be re-checked before a bearer token is sent. Adds tests for empty-stream host consultation/suppression on both streaming paths and for push rejecting a disallowed service_url. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --------- Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Multi-channel hosting samples
End-to-end samples for serving an agent-framework agent (or workflow)
through one or more channels with agent-framework-hosting.
The general hosting plumbing lives in
agent-framework-hosting; each channel is
its own package (agent-framework-hosting-responses,
agent-framework-hosting-invocations,
agent-framework-hosting-telegram, agent-framework-hosting-activity-protocol,
agent-framework-hosting-entra).
| Sample | What it shows | Packaging |
|---|---|---|
local_responses/ |
The minimal shape: one agent + one @tool + ResponsesChannel + a single run_hook that strips caller-supplied options and forces a reasoning preset. |
Local only. Start here to learn the run-hook seam. |
local_responses_workflow/ |
A 4-step Workflow (typed SloganBrief intake → writer → legal → formatter) hosted behind both the Responses and Invocations channels via a shared run_hook that parses inbound text/JSON into the workflow's typed input. The host writes per-conversation checkpoints via checkpoint_location=…. Demonstrates workflow targets + structured input adaptation + multi-channel + resume-across-turns. Includes a call_server.rest file with REST examples for both endpoints. |
Local only. |
foundry_hosted_agent/ |
One Foundry agent, Responses + Invocations only — the minimal shape that is runtime-compatible with the Foundry Hosted Agents platform. | Ships with Dockerfile + agent.yaml + agent.manifest.yaml + azure.yaml so the same image runs locally or as a Foundry Hosted Agent (azd up). |
foundry_telegram_invocations_weather/ |
Experimental Telegram weather bot that mounts TelegramChannel at POST /invocations, registers the Foundry Hosted Agents Invocations URL as the Telegram webhook, and uses FoundryHostedAgentHistoryProvider for storage. |
Ships with Dockerfile + agent.yaml + agent.manifest.yaml + azure.yaml; used to validate whether a non-Responses channel can run under Foundry Invocations. |
local_telegram/ |
Adds Telegram, a @tool, FileHistoryProvider, run hooks (per-user / per-chat session keying), extra Telegram commands, and ResponseTarget multicast. Runs under Hypercorn with multiple workers. |
Local only. No Dockerfile / Foundry packaging. |
local_identity_link/ |
Everything in local_telegram/ plus Teams and the Entra identity-link sidecar (/auth/start + /auth/callback). Demonstrates linking a Telegram chat to an Entra user so multiple non-Entra channels can share one isolation key. |
Local only. No Dockerfile / Foundry packaging. |
Each sample is fully self-contained — its own pyproject.toml, uv.lock,
server app.py, calling script(s), and storage/ directory. Every
sample uses [tool.uv.sources] to wire its agent-framework-hosting*
dependencies to the
feature/python-hosting
branch of the upstream repo via git refs, so they install cleanly outside
the monorepo while the hosting packages are still pre-PyPI. Once those
packages publish, drop the [tool.uv.sources] block and let the
declared deps resolve from PyPI.
Relationship to ../foundry-hosted-agents/
The sibling ../foundry-hosted-agents/ directory
contains samples for the agent-framework-hosted stack — agents
that run inside the Foundry Hosted Agents platform using its
built-in protocol surface (Responses, Invocations, conversation store,
isolation, identity), with no agent-framework-hosting package
involved.
| Aspect | af-hosting/ (this directory) |
foundry-hosted-agents/ |
|---|---|---|
| Server stack | agent-framework-hosting + per-channel packages (-responses, -invocations, -telegram, -activity-protocol, -entra) |
agent-framework-hosted only — the Foundry Hosted Agents runtime owns the HTTP surface |
| Channels other than Responses / Invocations | Yes — Telegram, Activity Protocol (Teams), Entra identity-linking | No — the platform exposes Responses + Invocations only |
| Run target | Local Hypercorn (local_responses/, local_telegram/, local_identity_link/); Hosted Agents or local (foundry_hosted_agent/) |
Hosted Agents or local container; targets the Hosted Agents platform contract |
| When to pick this | You need extra channels (Telegram/Teams via Activity Protocol/…), custom hosting middleware, or want to run outside the Foundry runtime | You only need Responses/Invocations and want zero hosting boilerplate, leveraging the Foundry-managed surface |
foundry_hosted_agent/ is the bridge sample: it uses the
agent-framework-hosting stack but is packaged so the Foundry Hosted
Agents platform can run it as one of its own.
See ARCHITECTURE.md for the cross-sample story.