diff --git a/docs/decisions/0027-hosting-channels.md b/docs/decisions/0027-hosting-channels.md new file mode 100644 index 00000000000..3870fb908c2 --- /dev/null +++ b/docs/decisions/0027-hosting-channels.md @@ -0,0 +1,144 @@ +--- +status: accepted +contact: eavanvalkenburg +date: 2026-06-11 +deciders: eavanvalkenburg +--- + +# Python minimal hosting core and pluggable channels + +## Context and Problem Statement + +Agent Framework has several protocol-specific hosting surfaces. App authors who want one agent or workflow on multiple protocols must compose servers, routes, middleware, session handling, and lifecycle code by hand. + +We will introduce a small Python hosting core that owns the common server shape and leaves protocol details inside channel packages. The first public contract must be intentionally narrow so Python can ship a base contract before adding identity linking, proactive delivery, or multicast behavior. Other language implementations may reuse the same conceptual boundary, but this ADR records the Python decision. + +## Decision Drivers + +- Keep the first host easy to explain: one app, one hostable target, one or more channels. +- Reuse Agent Framework's existing agent, workflow, session, history, and checkpoint primitives. +- Let channel packages own protocol parsing, protocol responses, authentication details, and native command surfaces. +- Make session continuity explicit through a channel-supplied `ChannelSession(isolation_key=...)`. +- Avoid approving cross-channel identity and delivery semantics before their safety model is reviewed. + +## Considered Options + +1. Keep only protocol-specific hosts. +2. Ship a large hosting core with identity linking, authorization, background delivery, active-channel routing, and multicast in v1. +3. Ship a minimal host/channel core now and track linking/multicast as follow-up work. + +### Keep only protocol-specific hosts + +- Good: no new abstraction or package surface. +- Neutral: each protocol can continue evolving independently. +- Bad: every multi-channel app still has to compose servers, lifecycle, and session handling by hand. + +### Ship the large cross-channel host in v1 + +- Good: the richest cross-channel scenarios are available immediately. +- Neutral: the host becomes the natural place to demonstrate identity and delivery policy. +- Bad: v1 becomes a security-sensitive identity and delivery system before the safety model is reviewed. + +### Ship the minimal core now + +- Good: the host/channel boundary can be implemented, tested, and explained without solving linking and durable delivery at the same time. +- Neutral: apps that need richer behavior must build it locally or wait for ADR-0028 follow-up work. +- Bad: proactive delivery and multicast scenarios are deliberately absent from v1. + +## Decision Outcome + +Chosen option: **minimal host/channel core now, follow-up enhancements later**. + +`AgentFrameworkHost` owns: + +- one application object, +- one hostable target (`SupportsAgentRun` agent-compatible object or a `Workflow`), and +- one or more channels. + +Channels own: + +- contributed routes, middleware, commands, and lifecycle callbacks, +- protocol-native request parsing into `ChannelRequest`, +- protocol-native rendering of the originating response, and +- any channel-specific authentication or signature validation. + +The host owns: + +- route/lifecycle aggregation, +- invocation of the target, +- `ChannelSession(isolation_key=...)` to `AgentSession` resolution and caching, +- `reset_session(isolation_key=...)`, +- host-level middleware, including Foundry isolation middleware only when the Foundry hosting environment flag is present, +- invocation of per-channel hooks (`ChannelRunHook`, `ChannelResponseHook`, `ChannelStreamUpdateHook`), and +- workflow checkpoint wiring through an explicit `checkpoint_location`. + +`ChannelIdentity`, when present, is request metadata only. In v1 it is not a linking, authorization, or delivery key. + +### Trust boundary for `isolation_key` + +The host treats `ChannelSession.isolation_key` as a session partition key, not as proof of identity. Channels or host middleware must authenticate and authorize any externally supplied value before passing it to the host. For example, a Responses caller must not be allowed to choose an arbitrary `previous_response_id` or header-derived key unless the platform or middleware has already established that the caller owns that conversation. The host deliberately does not infer that trust from the string itself. + +### Hook ownership + +Channels provide hook configuration and protocol-native context. The host invokes those hooks as part of the common invocation pipeline: + +- `ChannelRunHook` runs after channel parsing and before target invocation. +- `ChannelResponseHook` runs after target invocation and before the originating channel serializes its response. +- `ChannelStreamUpdateHook` is applied by the host while the channel consumes streamed updates because streaming serialization is protocol-specific. + +`ChannelStreamUpdateHook` is an update hook, not a final-response sanitizer. Channels that use it for redaction or filtering must also apply equivalent policy to any final response they render. Channels choose whether the response is streaming before run hooks execute. + +This keeps hook call conventions centralized while leaving protocol payload parsing and response formatting in channel packages. + +### State owned by v1 + +`state_dir` is limited to host-owned local files for reset-session aliases and workflow checkpoint path derivation. It does not store linked identities, active-channel state, response-routing state, continuation records, durable runner queues, or delivery attempts. Those storage concerns belong to ADR-0028. + +## Non-goals for v1 + +The following are deliberately **not** part of the v1 contract: + +- cross-channel identity linking (`IdentityLinker`, `local_identity_link`, or `agent-framework-hosting-entra`), +- identity allowlists or authorization policy (`IdentityAllowlist`, `AuthPolicy`), +- response routing beyond the originating channel (`ResponseTarget`, active channel, specific linked channel, `all_linked`), +- push or payload codecs (`ChannelPush`, `ChannelPushCodec`), +- background/continuation delivery, +- durable task runners (`DurableTaskRunner`, `InProcessTaskRunner`), +- retry/replay policy (`RetryPolicy`), +- fan-out, multicast, or all-linked delivery, +- confidentiality tiers and `LinkPolicy`, and +- a host-level multi-agent router. + +These areas are follow-up enhancements covered by [ADR-0028](0028-hosting-linking-multicast-enhancements.md). They are not prerequisites for shipping or using the v1 host. + +## Consequences + +Positive: + +- The host/channel model can be implemented and tested without designing a security-sensitive identity graph. +- Existing and new channel packages can share one Starlette app, middleware stack, lifecycle, and target invocation path. +- Session continuity is explicit and debuggable: two channels share history only when they produce the same `isolation_key`. +- Hook invocation is centralized in the host, so channels do not each invent the call convention. + +Negative: + +- Apps that need OAuth linking, allowlists, proactive messages, or multicast must continue to implement those behaviors outside the v1 host. +- Some richer cross-channel scenarios from the original design move to a separate decision and validation cycle. +- The host must document `isolation_key` trust clearly because it now provides the shared session boundary. + +## Validation Gates + +Before this ADR is accepted: + +- A sample can expose one target on multiple channels with one `AgentFrameworkHost` and no handwritten Starlette route composition. +- Built-in channel tests prove that routes, commands, startup, and shutdown callbacks are contributed by channels and aggregated by the host. +- Session tests prove that identical `ChannelSession.isolation_key` values resolve to the same cached `AgentSession`, and `reset_session` rotates that mapping. +- Channel tests prove that each channel renders only its own originating response; there is no host-level push, multicast, or active-channel delivery path. +- Workflow tests or samples use an explicit `checkpoint_location`. +- Foundry isolation middleware is documented and covered by integration or contract tests, including the non-Foundry case where raw isolation headers are ignored. +- The v1 API and packages do not expose the removed symbols or packages listed in [Non-goals for v1](#non-goals-for-v1). +- The Python spec is updated to match this simplified contract and uses "public", "stable", or "released" terminology for Agent Framework APIs. + +## More Information + +- Follow-up linking and multicast ADR: [ADR-0028](0028-hosting-linking-multicast-enhancements.md) diff --git a/docs/decisions/0028-hosting-linking-multicast-enhancements.md b/docs/decisions/0028-hosting-linking-multicast-enhancements.md new file mode 100644 index 00000000000..390881b7ea6 --- /dev/null +++ b/docs/decisions/0028-hosting-linking-multicast-enhancements.md @@ -0,0 +1,132 @@ +--- +status: proposed +contact: eavanvalkenburg +date: 2026-06-11 +deciders: eavanvalkenburg +--- + +# Hosting linking and multicast enhancements + +## Context and Problem Statement + +[ADR-0027](0027-hosting-channels.md) defines the minimal v1 hosting core: originating-channel responses, explicit `ChannelSession.isolation_key`, and no host-level identity linking, push, multicast, background delivery, or durable runners. + +This ADR tracks the richer cross-channel behaviors that were removed from v1. These enhancements are **follow-up work** and are **not prerequisites** for shipping, using, or stabilizing the v1 host/channel core. + +## Decision Drivers + +- Cross-channel continuity must not create accidental cross-user, cross-tenant, or cross-channel data leaks. +- Non-originating delivery must be observable, idempotent, retryable, and supportable. +- Protocol payloads must remain channel-native while still being safe to persist and replay. +- App authors need opt-in policy controls, not hidden defaults. +- The enhancement stack should layer on top of the v1 host without reshaping the minimal channel contract. + +## Enhancement Areas + +The follow-up design should cover these capabilities together because they share identity, storage, delivery, and replay concerns: + +- **Cross-channel identity linking** — a user can connect multiple `ChannelIdentity` values to one channel-neutral `isolation_key`. +- **Authorization and allowlist policy** — channels or hosts can require verified identity, allow specific native identities or claims, and deny unknown callers. +- **Non-originating response delivery** — a run can respond somewhere other than the request's originating protocol when explicitly configured. +- **Active-channel routing** — delivery can target the most recently observed linked channel for an `isolation_key`. +- **Multicast / all-linked delivery** — delivery can fan out to every linked channel or a selected set. +- **Background runs and continuation tokens** — long-running requests can return immediately and complete later, with a polling/status fallback. +- **Durable delivery runners** — delivery work can survive process restarts and support dead-letter handling. +- **Retry and replay semantics** — delivery attempts are bounded, deduplicated, and safe to replay. +- **Payload serialization** — channel-specific payloads can be persisted, redacted, versioned, and reconstructed without losing protocol fidelity. + +Candidate API names from the broader design (`IdentityLinker`, `IdentityAllowlist`, `AuthPolicy`, `ResponseTarget`, `ChannelPush`, `ChannelPushCodec`, `DurableTaskRunner`, `InProcessTaskRunner`, `RetryPolicy`, `LinkPolicy`) remain design vocabulary for this ADR. They are not approved v1 APIs. + +## Considered Options + +### Option A — Leave all behavior to applications + +Applications implement linking, authorization, push, retry, and serialization independently. + +- Good: the hosting core stays very small. +- Neutral: advanced apps can still build what they need. +- Bad: every app must solve the same security and delivery problems, likely inconsistently. + +### Option B — Add the full enhancement stack to v1 + +The first host release includes linking, authorization, active channel, multicast, background runs, durable runners, and codecs. + +- Good: the original cross-channel experience is available immediately. +- Neutral: samples can demonstrate rich end-to-end flows. +- Bad: v1 becomes security-sensitive, storage-heavy, and harder to stabilize. + +### Option C — Layer opt-in enhancement packages after v1 + +Ship the minimal host first, then add linking, authorization, and delivery packages behind explicit configuration. + +- Good: v1 remains simple while leaving room for a reviewed, supportable enhancement stack. +- Neutral: apps that need advanced delivery wait for follow-up packages. +- Bad: the first release does not satisfy proactive or all-linked scenarios. + +### Option D — Build only platform-specific integrations + +Implement linking and proactive delivery separately in Telegram, Activity Protocol, Discord, and future channels. + +- Good: each package can match its protocol exactly. +- Neutral: some shared abstractions may emerge later. +- Bad: cross-channel behavior becomes fragmented and hard to reason about. + +## Decision Outcome + +Proposed direction: **Option C — layered opt-in enhancement packages after v1**. + +The minimal host remains the foundation. Follow-up packages may add linking, authorization, delivery, and durable execution, but must be explicitly enabled and must pass the validation gates below before becoming part of the public contract. + +## Safety Requirements + +### Threat model + +The design must account for: + +- spoofed channel-native identities, +- stolen or replayed link challenges, +- cross-tenant or cross-confidentiality data leakage, +- unsolicited proactive messages, +- malicious payloads persisted for replay, +- denial-of-service through fan-out or retry storms, and +- privacy leakage through logs, metrics, or support tooling. + +Required mitigations include verified identity claims where available, signed and expiring link challenges, explicit user consent, per-channel capability checks, default-deny policy options, tenant partitioning, and uninformative denial messages on shared channels. + +### Idempotency and replay + +Exactly-once delivery is not a realistic guarantee. The design must provide: + +- stable run, continuation, and delivery-attempt identifiers, +- channel-level idempotency keys where protocols support them, +- bounded retry with jitter and explicit terminal states, +- replay windows and expiration, +- duplicate suppression for persisted attempts, and +- clear semantics for "delivered", "accepted by platform", and "observed by user". + +### Storage + +Enhancement storage must stay distinct from v1 `AgentSession` history and workflow checkpoints unless an implementation deliberately backs them with the same physical store. + +Stored data should be schema-versioned, minimized, encrypted or otherwise protected as appropriate, and partitioned by tenant/project. Link records, continuation records, active-channel state, delivery attempts, dead letters, and serialized payloads need independent TTL and deletion policies. + +### Observability and support + +The design must include structured logs, traces, and metrics for link attempts, authorization decisions, delivery scheduling, retries, replay, and dead-letter outcomes. Logs must avoid message content and sensitive identity claims by default. Operators need a way to inspect, revoke, replay, or purge stuck records safely. + +## Validation Gates + +Before these enhancements are accepted: + +- A reviewed threat model covers identity linking, authorization, non-originating delivery, multicast, and replay. +- Cross-channel linking tests prove a verified identity can link two channels and that unlink/deny paths do not leak information. +- Authorization tests cover native-id allowlists, verified-claim allowlists, default-deny behavior, and misconfiguration failures. +- Delivery tests cover originating-only, specific-channel, active-channel, selected-channel, and all-linked routing. +- Background/continuation tests cover polling fallback, cancellation or expiration, process restart, retry, and dead-letter behavior. +- Codec tests prove payloads are versioned, redacted where needed, backward compatible, and rejected safely when unknown. +- Multicast tests prove fan-out is bounded, independently retried, and idempotent per destination. +- Observability tests or manual validation prove support operators can correlate a request to delivery attempts without exposing sensitive content. + +## Relationship to ADR-0027 + +ADR-0027 remains valid without any of these enhancements. This ADR extends the hosting model only after the safety, storage, and support requirements above are satisfied. diff --git a/docs/specs/002-python-hosting-channels.md b/docs/specs/002-python-hosting-channels.md new file mode 100644 index 00000000000..fbfaa0a8145 --- /dev/null +++ b/docs/specs/002-python-hosting-channels.md @@ -0,0 +1,320 @@ +--- +status: proposed +contact: eavanvalkenburg +date: 2026-06-11 +deciders: eavanvalkenburg +--- + +# Python hosting core and pluggable channels + +## Scope + +This specification is the Python implementation plan for [ADR-0027](../decisions/0027-hosting-channels.md). It documents the simplified v1 host/channel contract only. + +The v1 contract is: + +- `AgentFrameworkHost` owns one Starlette app, one hostable target, and one or more channels. +- A hostable target is either a `SupportsAgentRun`-compatible agent or a `Workflow`. +- Channels contribute routes, middleware, commands, and lifecycle callbacks. +- Channels parse protocol-native input into `ChannelRequest`. +- Channels render their own originating response. +- Session continuity is explicit: a channel supplies `ChannelSession(isolation_key=...)`, and the host resolves/caches an `AgentSession` for that key. +- The host invokes `ChannelRunHook` and `ChannelResponseHook`; channels provide hook configuration and protocol context. + +The host does not link identities, route responses to other channels, run background continuations, or multicast in v1. Those enhancements are tracked in [ADR-0028](../decisions/0028-hosting-linking-multicast-enhancements.md). + +## Goals + +- Let an app expose one agent or workflow on multiple protocols without handwritten Starlette composition. +- Keep protocol parsing and response formatting inside channel packages. +- Provide one session-resolution path shared by all channels. +- Keep the channel authoring surface small enough for new channels to implement. +- Preserve full-fidelity agent and workflow results until a channel decides how to render them. + +## Non-goals for v1 + +The following are removed from the v1 implementation pass: + +- `IdentityLinker`, `IdentityAllowlist`, `AuthPolicy`, and `LinkPolicy` +- `ResponseTarget`, active-channel routing, `all_linked`, fan-out, and multicast +- `ChannelPush` and `ChannelPushCodec` +- `DurableTaskRunner`, `InProcessTaskRunner`, and `RetryPolicy` +- continuation tokens and background delivery +- confidentiality tiers +- `agent-framework-hosting-entra` +- `local_identity_link` + +These are follow-up design topics, not hidden requirements of the v1 host. + +## Packages + +| Package | Import surface | Contents | +|---|---|---| +| `agent-framework-hosting` | `agent_framework_hosting` | `AgentFrameworkHost`, channel protocols, key request/result types, hooks, `reset_session`, state-path helpers. | +| `agent-framework-hosting-responses` | `agent_framework_hosting_responses` | `ResponsesChannel`. | +| `agent-framework-hosting-invocations` | `agent_framework_hosting_invocations` | `InvocationsChannel`. | +| `agent-framework-hosting-telegram` | `agent_framework_hosting_telegram` | `TelegramChannel` and Telegram command helpers. | +| `agent-framework-hosting-activity-protocol` | `agent_framework_hosting_activity_protocol` | `ActivityProtocolChannel` for Activity Protocol over Azure Bot Service. | +| `agent-framework-hosting-discord` | `agent_framework_hosting_discord` | `DiscordChannel` and Discord command/interaction helpers. | +| `agent-framework-foundry-hosting` | `agent_framework.foundry_hosting` | Foundry isolation middleware and Foundry-backed hosting helpers usable with the v1 host. | + +Channel packages may depend on their native SDKs. The core hosting package should not depend on channel SDKs or on top-level legacy protocol hosts. + +## Key Types + +### `AgentFrameworkHost` + +The host constructor accepts: + +- `target`: one `SupportsAgentRun`-compatible object or one `Workflow` +- `channels`: one or more `Channel` instances +- optional Starlette middleware +- optional `state_dir` +- optional workflow `checkpoint_location` + +The host exposes: + +- `app`: the canonical Starlette ASGI application +- `serve(...)`: a convenience wrapper for local serving +- `reset_session(isolation_key: str)`: rotate the cached `AgentSession` for a host-tracked conversation + +`state_dir` is narrowed to v1 host-owned local files only: + +- session aliases (`isolation_key` to current `AgentSession` id), and +- workflow checkpoint paths when the app chooses the host-provided file layout. + +It is not a store for identity links, continuations, active-channel state, delivery attempts, or multicast payloads. + +Externally supplied isolation keys are trusted only after the channel or host middleware has authenticated and authorized the caller. The host uses `isolation_key` as a partition key; the string itself is not proof of identity or ownership. + +### `Channel` + +A channel implements a small protocol: + +- declare a stable channel id/name, +- contribute routes, middleware, commands, and lifecycle callbacks, +- parse inbound protocol data into `ChannelRequest`, +- call the host through `ChannelContext.run(...)` or `ChannelContext.run_stream(...)`, and +- serialize the returned result to the originating protocol response. + +Channels own protocol authentication, signature validation, native command registration, and protocol-specific error bodies. + +### `ChannelContribution` + +`ChannelContribution` is the channel's host-facing contribution: + +- Starlette routes and optional middleware, +- native command descriptors, +- startup and shutdown callbacks, and +- any channel-local metadata needed by the package. + +The host aggregates contributions but does not interpret protocol payloads. + +### `ChannelRequest` + +`ChannelRequest` is the host-neutral request envelope produced by a channel. It carries: + +- target input, +- optional `ChannelSession`, +- optional `ChannelIdentity`, +- options and attributes produced by the channel, and +- request metadata useful to hooks and context providers. + +The host may pass attributes through to context providers and middleware. Channels should treat attributes as a documented extension bag, not as a cross-channel delivery contract. + +### `ChannelSession` + +`ChannelSession(isolation_key=...)` is the only v1 session-continuity mechanism. + +When a request contains an isolation key: + +1. The host looks up or creates the cached `AgentSession` for that key. +2. The target runs with that `AgentSession` when the target is an agent. +3. `reset_session(isolation_key)` rotates the alias so the next request starts a new conversation. + +If two channels produce the same isolation key on the same host, they share the same cached session. If they produce different keys, they do not share session state. + +### `ChannelIdentity` + +`ChannelIdentity` is optional request metadata such as channel id, native user id, tenant id, claims, or display attributes. + +In v1, `ChannelIdentity` does not link channels, authorize callers, select delivery destinations, or imply that two identities should share an `AgentSession`. A channel that wants shared history must still produce the same `ChannelSession.isolation_key`. + +### Hooks + +Hooks are optional and channel-owned: + +- `ChannelRunHook`: runs after channel parsing and before host invocation; returns the `ChannelRequest` to execute. +- `ChannelResponseHook`: runs after target completion and before the originating channel renders a one-shot response. +- `ChannelStreamUpdateHook`: the host applies it to streamed updates before the originating channel serializes the stream. + +Common uses include adapting chat text into workflow inputs, enforcing deployment-specific options, flattening rich output for text-only protocols, or filtering streamed updates for a protocol. Stream update hooks are update-only; they do not automatically sanitize `get_final_response()` output. Channels choose their response transport from the parsed protocol request before invoking run hooks. + +### `HostedRunResult` + +`HostedRunResult[T]` wraps the target's full-fidelity result plus the resolved `AgentSession | None`. + +- Agent targets produce `HostedRunResult[AgentResponse]`. +- Workflow targets produce `HostedRunResult[WorkflowRunResult]`. + +The host does not flatten, filter, or translate the result. Each channel decides how much of the result its protocol can carry. + +## Host Behavior + +1. `AgentFrameworkHost` builds one Starlette app and asks each channel for its contribution. +2. A channel route receives a protocol-native request. +3. The channel validates/parses the native payload and creates `ChannelRequest`. +4. The channel passes the request, optional `ChannelRunHook`, and protocol-native context to the host. +5. The host invokes `ChannelRunHook`, if configured, and receives the prepared request. +6. The host resolves an `AgentSession` from `ChannelSession.isolation_key` when present. +7. The host invokes the agent or workflow target. +8. The host wraps the result in `HostedRunResult` or the streaming equivalent. +9. The host invokes `ChannelResponseHook`, if configured, for non-streaming/final response shaping. +10. The host applies stream update hooks while the channel consumes streams; the channel renders the originating protocol response. + +There is no host-level route from one channel's request to another channel's response in v1. + +## Workflow Checkpoints + +Workflow checkpointing is explicit. Apps either configure checkpoint storage on the workflow itself or pass a `checkpoint_location` to the host so the workflow dispatch path can use the intended file location. + +`state_dir` may provide a conventional location for workflow checkpoint files, but checkpointing is still opt-in and separate from agent session history. Checkpoints are workflow-runtime state, not channel state and not identity-link state. + +## Foundry Isolation Middleware + +V1 keeps Foundry isolation as middleware rather than as a channel-linking feature. + +The middleware is installed only when the Foundry hosting environment flag is present. In that environment it reads Foundry-provided isolation values at the trusted hosting boundary, exposes them as read-only request context for Foundry-aware history or memory providers, and rejects unsafe session resumes when the live isolation context does not match persisted session context. Outside Foundry, raw isolation headers are ignored unless an app supplies its own trusted middleware. + +This middleware does not create cross-channel identity links and does not authorize non-Foundry channels. + +## Current Channels + +### Responses + +`ResponsesChannel` exposes the OpenAI-compatible Responses API shape. It maps request body fields such as input, options, and conversation identifiers into `ChannelRequest`, and it renders Responses-compatible one-shot or streaming responses. + +Responses session continuity uses a channel-selected `isolation_key`, commonly derived from a response/conversation id, caller-provided session id, Foundry isolation context, or deployment-specific request metadata. + +### Invocations + +`InvocationsChannel` exposes an invocation endpoint for server-side callers and tools. It maps the request body into `ChannelRequest` and renders the invocation result on the same HTTP response. + +Invocations is useful for typed workflow inputs because a `ChannelRunHook` can translate the request body into the workflow's expected input type. + +### Telegram + +`TelegramChannel` supports webhook or polling transport, native command registration, and message rendering back to the originating Telegram chat. + +The channel chooses a default `isolation_key` from Telegram-native data such as chat id, user id, or a configured user/chat scope. A `/new` or equivalent command may call `reset_session` for that isolation key. + +### Activity Protocol + +`ActivityChannel` supports Activity Protocol requests, typically through Azure Bot Service for Teams, Web Chat, and other Bot Framework-fronted surfaces. + +The channel maps incoming `Activity` objects to `ChannelRequest` and renders a reply activity to the originating conversation. Proactive Activity delivery, active-channel routing, and all-linked fan-out are not v1 host semantics. + +### Discord + +`DiscordChannel` supports Discord messages, slash commands, and interactions as channel-native input. + +The channel maps Discord-native user, guild, channel, thread, and interaction data into `ChannelRequest` metadata and a configured `ChannelSession.isolation_key`. It renders the result to the originating Discord response path. + +## High-level Samples + +### One agent on Responses + +```python +host = AgentFrameworkHost( + target=agent, + channels=[ResponsesChannel()], +) + +app = host.app +``` + +### One agent on multiple channels + +```python +host = AgentFrameworkHost( + target=agent, + channels=[ + ResponsesChannel(), + InvocationsChannel(), + TelegramChannel(bot_token=os.environ["TELEGRAM_BOT_TOKEN"]), + ], +) + +host.serve(host="localhost", port=8000) +``` + +The host owns one Starlette app. Each channel contributes its own routes and renders its own response. + +### Adapting a request before execution + +```python +from dataclasses import replace + + +def enforce_options(request: ChannelRequest) -> ChannelRequest: + options = dict(request.options or {}) + options["temperature"] = 0 + return replace(request, options=options) + + +host = AgentFrameworkHost( + target=agent, + channels=[ResponsesChannel(run_hook=enforce_options)], +) +``` + +### Workflow with explicit checkpoints + +```python +host = AgentFrameworkHost( + target=workflow, + channels=[InvocationsChannel(run_hook=adapt_to_workflow_input)], + checkpoint_location=Path("./.af-hosting/workflow_checkpoints"), +) +``` + +The hook adapts channel-native input to the workflow's typed input. Checkpoints use the explicit workflow checkpoint location, not identity-link or delivery storage. + +### Message channel reset command + +```python +async def new_chat(context): + if context.request.session is not None: + await context.host.reset_session(context.request.session.isolation_key) + await context.reply("Started a new conversation.") +``` + +Telegram, Activity Protocol, and Discord can expose equivalent native commands when their protocols support them. + +## Follow-up Enhancements + +See [ADR-0028](../decisions/0028-hosting-linking-multicast-enhancements.md) for the deferred design covering: + +- cross-channel identity linking, +- authorization and allowlists, +- non-originating response delivery, +- active-channel routing, +- multicast and all-linked delivery, +- background runs and continuation tokens, +- durable delivery runners, +- retry/replay semantics, and +- payload serialization. + +Those enhancements must layer on top of this v1 contract without requiring v1 users to adopt them. + +## Validation Gates + +The Python implementation should be considered complete when: + +- a sample uses one `AgentFrameworkHost` with multiple channels and no manual Starlette route composition, +- each current channel has contract tests for route contribution, lifecycle, request parsing, hooks, and originating response rendering, +- session tests prove shared `isolation_key` values share an `AgentSession` and `reset_session` rotates it, +- workflow tests or samples use explicit `checkpoint_location`, +- Foundry isolation middleware is covered by integration or contract tests, +- no v1 package exposes the removed linking, multicast, durable-runner, or continuation APIs, and +- this spec and ADR-0027 remain aligned.