* samples(hosting): add hosting Channels sample apps under samples/04-hosting/af-hosting Adds five end-to-end sample apps under ``python/samples/04-hosting/af-hosting/`` that exercise the ``agent-framework-hosting`` Channels stack from the simplest single-channel case up to a multi-channel deployment with cross-channel identity linking. Samples (ordered by complexity) ------------------------------- * ``foundry_hosted_agent/`` — minimal Responses + Invocations host with a Foundry-backed agent and ``FoundryHostedAgentHistoryProvider``. ``agd``-deployable; bundles a ``Dockerfile`` and ``scripts/vendor-packages.sh`` that copies workspace packages into ``_vendor/`` for self-contained builds. ``_vendor/`` is gitignored. * ``local_responses/`` — single-channel Responses host with a ``run_hook`` that strips caller-supplied options and forces a reasoning preset. Demonstrates the hook seam over the uniform ``ChannelRequest`` envelope. * ``local_responses_workflow/`` — Responses + Invocations exposing a three-agent workflow with per-conversation checkpoint storage. * ``local_telegram/`` — Responses + Telegram with a ``@tool``, ``FileHistoryProvider``, hooks, and a ``ResponseTarget`` multicast variant (``call_server_multicast.py``) that pushes a single Responses reply to a separate Telegram chat. * ``local_identity_link/`` — full surface: Responses + Invocations + Telegram + Activity Protocol (Teams) + the ``EntraIdentityLinkChannel`` sidecar. Resolves per-channel ids onto a single Entra object id so a user's history follows them across surfaces. Notes ----- * Samples that use Telegram/Teams via Activity Protocol depend on the renamed ``agent-framework-hosting-activity-protocol`` package (see the PR-5 series). * All samples use ``[tool.uv.sources]`` editable workspace deps, except ``foundry_hosted_agent/`` which uses the ``./_vendor/`` self-contained layout for ``azd`` Docker builds. * Each sample includes a ``README.md`` with run instructions and an ``app.py`` ASGI entrypoint plus a ``call_server.py`` client harness. Depends on the prior hosting PRs (foundry-hosted-agent refactor + hosting-core + the per-channel packages). After those merge, this branch can be rebased onto ``main`` cleanly. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * samples(hosting): point sample deps at the feature/python-hosting GitHub branch Switches every sample's ``[tool.uv.sources]`` from in-monorepo editable path deps (which only resolve when running inside the agent-framework workspace) to git refs targeting the ``feature/python-hosting`` branch on ``microsoft/agent-framework``. Samples now install standalone outside the monorepo while the ``agent-framework-hosting*`` packages are still pre-PyPI; once they publish, the ``[tool.uv.sources]`` block can be dropped and the declared deps resolve from PyPI. Cleanup ------- * Drops ``foundry_hosted_agent/scripts/vendor-packages.sh``, ``_vendor/`` from ``.gitignore``, the ``hooks.prepackage`` block in ``azure.yaml`` and the ``COPY _vendor/`` step in the Dockerfile — vendoring is no longer needed because git refs make the deps network-resolvable from any context. * Drops obsolete ``workspace.pyproject.toml`` reference and ``scripts/`` / ``workspace.pyproject.toml`` entries from ``Dockerfile.dockerignore``. * Updates the foundry sample's Dockerfile to ``uv sync --no-dev`` (no ``--frozen``) so it locks fresh against the GitHub-hosted deps at build time. * Drops every committed ``uv.lock`` because the resolver needs network access to ``feature/python-hosting`` to lock — they regenerate the first time a user runs ``uv sync`` after the branch lands. * Refreshes the per-sample READMEs to mention the GitHub install path instead of "in-tree workspace packages". Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * samples(hosting): address PR #5645 review comments - foundry_hosted_agent/call_server.py: replace hard-coded project_endpoint and service_session_id with FOUNDRY_PROJECT_ENDPOINT, FOUNDRY_HOSTED_AGENT_NAME, and optional FOUNDRY_HOSTED_SESSION_ID environment variables. Session-id is now optional so the sample exercises the new-conversation path by default. - local_identity_link/app.py: * make_telegram_hook: apply the reasoning bump regardless of identity-link state (the previous early-return on linked chats silently dropped the high-effort preset for the very flow the sample exists to demonstrate). * make_responses_hook: add a prominent DEV-ONLY warning that the client-supplied entra_oid shortcut bypasses identity verification and must be replaced by a JWT validator in production. * /link command: early-return when chat_id is missing instead of minting an authorize URL keyed on "telegram:None" (which would poison the link store with a binding any future chat_id-less update would collapse onto). * Switch ENTRA_CERT_PATH / ENTRA_CERT_PASSWORD env vars to the longer ENTRA_CERTIFICATE_PATH / ENTRA_CERTIFICATE_PASSWORD names that the README already documents. * channels: Sequence[Channel] -> list[Channel] (the next line appends, which a Sequence type doesn't expose). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * chore(hosting-samples): apply sample formatting Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * fix(hosting-samples): guard command input text Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --------- Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
local_identity_link — every channel, plus identity linking
The full surface: Responses + Invocations + Telegram + Activity Protocol (Teams) + the Entra
identity-link sidecar. The Entra channel exposes
/auth/start + /auth/callback so users on Telegram (or any non-Entra
channel) can bind their per-channel id to a stable entra:<oid> isolation
key. Channel run-hooks then rewrite incoming requests to use the linked
key, so a chat started on Telegram and a chat started on Teams that both
resolve to the same Entra user share one history.
Run
export FOUNDRY_PROJECT_ENDPOINT=https://<your-project>.services.ai.azure.com
export FOUNDRY_MODEL=gpt-4o
export TELEGRAM_BOT_TOKEN=...
# Entra app registration (confidential client):
export ENTRA_TENANT_ID=...
export ENTRA_CLIENT_ID=...
export ENTRA_CLIENT_SECRET=... # or:
# export ENTRA_CERTIFICATE_PATH=./teams-bot.pem
export PUBLIC_BASE_URL=https://<public-host> # used to mint redirect_uri
# Teams (optional — same tenant):
export TEAMS_APP_ID=...
export TEAMS_APP_PASSWORD=...
az login
uv sync
uv run hypercorn app:app \
--bind 0.0.0.0:8000 \
--workers 4
Identity link
Register https://<public-host>/auth/callback as the redirect URI on your
Entra app, then visit (replace <chat_id> with the Telegram numeric
chat id):
https://<public-host>/auth/start?channel=telegram&id=<chat_id>
After sign-in, subsequent Telegram messages from that chat resolve to the linked Entra user.
Call locally
uv sync --group dev
# Default: post a Responses request as `local-dev`.
uv run python call_server.py "What is the weather in Tokyo?"
# Resume any session by id, including a Telegram one (works because
# the Telegram run-hook writes sessions under telegram:<chat_id>):
uv run python call_server.py --previous-response-id telegram:8741188429 "What did we discuss?"
# Multicast to a Telegram chat in parallel with the local response:
uv run python call_server.py --telegram-chat-id 8741188429 "Heads up."
This sample is local-only — it shows the
agent-framework-hostingserver stack as a standalone process. For a Foundry-Hosted-Agents-compatible packaging (Dockerfile +agent.yaml+azure.yaml), seefoundry_hosted_agent/.