* feat(hosting-telegram): add Telegram channel package
New ``agent-framework-hosting-telegram`` package implementing the
Telegram Bot API channel for the Hosting framework. Mounts a webhook
endpoint (``POST /telegram/webhook``) and an in-process polling loop
onto an ``AgentFrameworkHost`` and translates Telegram ``Update``
payloads to/from the channel-neutral ``ChannelRequest`` /
``HostedRunResult`` plumbing.
Surface (re-exported from ``agent_framework_hosting_telegram``):
- ``TelegramChannel`` -- concrete ``Channel`` implementation. Owns the
webhook route + an optional ``getUpdates`` long-polling lifespan,
parses Telegram ``Update``s into ``ChannelRequest`` (text, photo,
document, voice, callback_query, …), runs the optional
``ChannelRunHook``, calls back into the ``ChannelContext`` to invoke
the agent target, and posts the response back via
``sendMessage`` / ``sendChatAction`` / ``answerCallbackQuery`` on the
Telegram Bot API. Honours ``DeliveryReport.include_originating`` so
cross-channel pushes can target the originating Telegram chat
without double-acking.
- Native fields the channel doesn't lift onto ``ChannelRequest`` (e.g.
``chat.type``, ``message.message_id``, ``callback_query.data``) are
attached to ``ChannelRequest.attributes`` so a ``ChannelRunHook``
can pick them up via the standard ``protocol_request=`` kwarg.
- 13 unit tests covering route wiring, ``Update`` parsing across the
common content shapes, hook composition, and originating vs
non-originating delivery branches.
Registers the package in ``python/pyproject.toml``
``[tool.uv.sources]`` and adds the matching pyright
``executionEnvironments`` entry. Stacks on PR-2 (Hosting core);
independent of PR-3 / PR-4.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* fix(hosting-telegram): preserve in-chat ordering, ack-before-run, drain shutdown
- Replace per-update task fan-out with per-chat asyncio.Queue + worker.
Telegram only guarantees update ordering up to getUpdates; the
previous code spawned one task per update, which broke ordering for
adjacent updates in the same chat. Updates are now serialised per
chat_id (so /start then "what's the weather" can't race) while
different chats still process in parallel.
- Webhook handler now acks (200) immediately and runs the agent in
the per-chat worker. Telegram redelivers any update the webhook
doesn't 200 within ~60 seconds, so a streamed agent reply that runs
longer than that previously triggered a retry storm and duplicate
replies.
- _on_shutdown now drains everything: poll task → per-chat workers →
webhook-spawned dispatcher tasks (the new ack-before-run path), then
deletes the webhook + closes the HTTP client. Previously webhook
tasks were not tracked at all, so an in-flight agent invocation
could leak past app shutdown.
- _enqueue_update extracts chat_id from message / edited_message /
callback_query; updates with no resolvable chat fall back to a
one-shot dispatcher task that's still tracked in _update_tasks for
shutdown.
- Webhook handler now also returns 400 on malformed JSON / non-object
payloads instead of crashing the request.
4 new tests cover per-chat serial ordering, parallel-across-chats
isolation, ack-before-run latency, and shutdown drain.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* test(hosting): drop redundant @pytest.mark.asyncio decorators
asyncio_mode = "auto" is configured in pyproject.toml across the
hosting packages, so individual @pytest.mark.asyncio decorators are
unnecessary.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* fix(hosting-telegram): adapt push tests to hosted run result wrapper
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* feat(hosting-telegram): add response hooks
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
---------
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>