Skip to content

feat(channels): stream outbound messages - #2180

Open
cjol wants to merge 1 commit into
mainfrom
docs/channel-streaming-rfc
Open

feat(channels): stream outbound messages#2180
cjol wants to merge 1 commit into
mainfrom
docs/channel-streaming-rfc

Conversation

@cjol

@cjol cjol commented Aug 28, 2026

Copy link
Copy Markdown
Contributor

This PR adds outbound message streaming to @cloudflare/channels, including native Slack and Telegram streaming, composite routing, and an AI SDK adapter.

Why

  • Channels currently wait for a complete model response before delivering anything, even when the destination supports progressive rendering.
  • Streaming needs transport-neutral chunks because provider-specific payloads do not compose across Slack, Telegram, fallback routes, and fanout routes.
  • A Channel owns stream consumption and terminal provider calls. This ensures an interrupted generation still finalizes its provider message instead of leaving a draft or streaming message unfinished.
  • Channels that cannot stream collect the text and use ordinary delivery, so callers use one Host API regardless of transport capability.
  • Slack cannot stream at the top level of an ordinary channel. The adapter collects and posts one ordinary message there, while threads and direct messages use Slack's native streaming methods.

Public API Surface

Symbol Kind Notes
ChannelChunk Type Neutral text, reasoning, tool, and source stream chunks
ChannelChunkSource Type Normalized ReadableStream<ChannelChunk> input
ChannelDeliveryOptions Type Wraps caller-owned delivery context
ChannelStreamOptions Type Supplies an optional title and delivery context
Channel.stream Optional method Lets a Channel consume and finalize a streamed answer
OutboundResolver.stream Method Recursively streams through composite Channels
ChannelHost.stream Method Resolves a destination and streams or collects before delivery
consumeChunks Function Finalizes exactly once after normal or interrupted consumption
ChunkConsumer Type Defines chunk and finalization callbacks
StreamOutcome Type Reports normal or interrupted stream completion
toChannelChunks Function Maps an AI SDK fullStream to Channel chunks

DeliveryResult now permits a reference on its uncertain arm.

deliver now takes ChannelDeliveryOptions as its third argument instead of a bare ChannelDeliveryContext.

Architectural Changes

ReadableStream<ChannelChunk>
             |
        ChannelHost
             |
       +-----+------+
       |            |
 Channel.stream   collect text
       |            |
 native provider  Channel.deliver

Composite Channels preserve the same contract:

  • fanout tees the source so each destination receives its own stream.
  • fallback retains consumed chunks and replays them after a confirmed failure.
  • uncertain stops fallback to avoid potentially duplicating partial delivery.

Code Changes

  • Slack uses chat.startStream, chat.appendStream, and chat.stopStream for threads and direct messages. Top-level channel streams collect into chat.postMessage.
  • Telegram uses sendMessageDraft for private-chat previews and always finishes with sendMessage, including after an interrupted generation.
  • The Host collects text for Channels without native streaming support and uses TextSegmentJoiner to preserve semantic spacing around tool and metadata boundaries.
  • The AI SDK adapter maps fullStream parts into neutral chunks and turns abort or error parts into abnormal stream completion.
  • Live fixtures independently observe Telegram, Slack top-level, Slack threads, and email through one shared provider matrix.

Compatibility

The third argument to deliver changed from:

ChannelDeliveryContext

to:

ChannelDeliveryOptions

Callers passing a delivery ID must wrap it:

host.deliver(surface, message, {
  delivery: { deliveryId }
});

Devin Review

@changeset-bot

changeset-bot Bot commented Aug 28, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: cc42f11

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 1 package
Name Type
@cloudflare/channels Minor

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@devin-ai-integration devin-ai-integration Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔍 Devin Review: 1 flag

Not posted on this PR by your GitHub settings — view it in Devin Review. (Configure)

Devin Review

@pkg-pr-new

pkg-pr-new Bot commented Aug 28, 2026

Copy link
Copy Markdown

Open in StackBlitz

agents

npm i https://pkg.pr.new/agents@2180

@cloudflare/ai-chat

npm i https://pkg.pr.new/@cloudflare/ai-chat@2180

@cloudflare/codemode

npm i https://pkg.pr.new/@cloudflare/codemode@2180

hono-agents

npm i https://pkg.pr.new/hono-agents@2180

@cloudflare/shell

npm i https://pkg.pr.new/@cloudflare/shell@2180

@cloudflare/think

npm i https://pkg.pr.new/@cloudflare/think@2180

@cloudflare/voice

npm i https://pkg.pr.new/@cloudflare/voice@2180

@cloudflare/worker-bundler

npm i https://pkg.pr.new/@cloudflare/worker-bundler@2180

commit: cc42f11

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant