Commit Graph

3 Commits

  • Simplify Python hosting core (#6492)
    Remove linking, multicast, durable delivery, and host push machinery from the v1 hosting core. Keep those scenarios in a proposed follow-up ADR and update channel packages, samples, docs, tests, and workspace metadata around the smaller host/channel contract.
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
  • Python: feat(python): cross-channel hosting improvements (endpoint paths, Activity push, Telegram/Teams fixes) (#6307)
    * 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>
  • Python: add agent-framework-hosting-telegram channel (#5643)
    * 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>