Skip to content

feat(apps): support resetting response streams and streaming error handling#635

Merged
lilyydu merged 6 commits into
mainfrom
lilyydu/stream-updates
Jul 9, 2026
Merged

feat(apps): support resetting response streams and streaming error handling#635
lilyydu merged 6 commits into
mainfrom
lilyydu/stream-updates

Conversation

@lilyydu

@lilyydu lilyydu commented Jul 8, 2026

Copy link
Copy Markdown
Contributor

Summary

Ports two merged teams.py PRs to TypeScript:

Streaming error handling (port of #453)

  • Add TerminalStreamError, StreamTimedOutError, and StreamNotAllowedError (in types/streamer.ts).
  • HttpStream.send() now inspects the 403 response body and maps it to the right error instead of always treating a 403 as canceled:
    • exceeded streaming timeStreamTimedOutError (marks timedOut)
    • cancelStreamCancelledError
    • not allowedStreamNotAllowedError
    • anything else / empty body → TerminalStreamError
  • On a 2-minute streaming timeout, the stream finalizes by updating the original message in place — it drops the streaminfo entity and stream channelData so the send routes through update (reusing the id) rather than posting a duplicate.
  • Chunk sends swallow a timeout mid-stream; close() then sends the buffered content as a plain final message.
  • retry gains a nonRetryable option so terminal stream errors are not retried.

Reusable streams (port of #495)

  • Emitting or updating after close() reopens the stream on the same instance, starting a new streamed message.
  • close() is idempotent until the next emit/update.
  • app.process.ts close listener switched from onceon so each reused-stream close fires.

Example

Adds examples/stream, mirroring the teams.py example's final state:

  • default message → single-stream demo with suggested actions
  • simple-card → sends a minimal Adaptive Card outside the streaming flow
  • multi-stream → emits an Adaptive Card as stream 1's final message, close()s, then reuses ctx.stream for a second streamed response

Tests

  • packages/apps http-stream spec extended: 403 message→error mapping (parametrized), empty-body 403 → terminal error, final-send timeout updates in place, mid-stream timeout updates in place, and emit-after-close stream reuse.
  • Full packages/apps suite passes (294 tests); tsc and eslint clean across the changed library files and the new example.

Notes

  • The Python-only uv.lock change was not ported.
  • examples/stream/.env is gitignored (no credentials committed).

…ndling

Port of teams.py PRs #495 and #453 to TypeScript.

Streaming error handling (#453):
- Add TerminalStreamError, StreamTimedOutError, and StreamNotAllowedError
- Map the 403 error body to the right error (timed out / canceled / not
  allowed / terminal) instead of always treating a 403 as canceled
- On a 2-minute streaming timeout, finalize by updating the original
  message in place (drop streamInfo + stream channelData) rather than
  posting a duplicate
- Add a nonRetryable option to the retry util so terminal stream errors
  are not retried

Reusable streams (#495):
- Emitting or updating after close() reopens the stream on the same
  instance, starting a new streamed message
- close() is idempotent until the next emit/update; app processor close
  listener switched from once to on so each reused close fires

Add the stream example demonstrating single-stream, simple-card, and
multi-stream (Adaptive Card + close + reuse) flows.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot AI review requested due to automatic review settings July 8, 2026 20:41
Adds the @examples/stream workspace entry so `npm ci` stays in sync
with package.json.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

Copilot AI 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.

Pull request overview

Ports streaming behavior improvements from teams.py into @microsoft/teams.apps, adding richer handling of terminal streaming failures (403 variants), support for reusing a stream instance after close(), and a new examples/stream sample to demonstrate multi-stream flows.

Changes:

  • Add terminal streaming error types and route 403 streaming responses to specific errors (timeout/cancel/not-allowed/terminal).
  • Make HttpStream.close() idempotent and allow emit/update after close to restart streaming on the same instance; update processor close listener accordingly.
  • Add a new examples/stream sample showcasing single-stream, simple-card, and multi-stream usage.

Reviewed changes

Copilot reviewed 13 out of 16 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
packages/apps/src/utils/promises/retry.ts Adds nonRetryable option to stop retrying specific error classes.
packages/apps/src/types/streamer.ts Introduces terminal stream error classes and adds closed to the streamer contract.
packages/apps/src/http/http-stream.ts Implements timeout/not-allowed error mapping, idempotent/reusable close behavior, and finalization behavior after timeout.
packages/apps/src/http/http-stream.spec.ts Expands test coverage for 403 error mapping, timeout finalization behavior, and stream reuse after close.
packages/apps/src/app.process.ts Switches close listener to .on('close') to support reused streams firing close multiple times.
examples/stream/turbo.json Adds turbo task config for the new stream example.
examples/stream/tsconfig.json Adds TypeScript config for the new stream example.
examples/stream/src/index.ts Implements the stream demo app (single-stream, simple-card, multi-stream).
examples/stream/README.md Documents how to run and test the stream example.
examples/stream/package.json Adds the example package definition and scripts.
examples/stream/eslint.config.js Adds eslint config wiring for the example.
examples/stream/appPackage/manifest.json Adds a Teams app manifest for the example bot.
examples/stream/.gitignore Adds ignore rules for generated/dev files in the example.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread packages/apps/src/http/http-stream.ts
Comment thread packages/apps/src/http/http-stream.ts
Comment thread packages/apps/src/http/http-stream.spec.ts
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
…reamError

The 'Content stream is not allowed on an already completed streamed
message' 403 shares the ContentStreamNotAllowed error code and the
'not allowed' substring with the generic stream-not-allowed error, so
it was incorrectly mapped to StreamNotAllowedError. Guard the 'not
allowed' branch against 'completed streamed message' so this terminal
case falls through to TerminalStreamError.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The main merge (#634, flatten client method chains) replaced the
chained client.conversations.activities().create/.update API with
flattened client.conversations.createActivity/updateActivity. Six
stream tests still mocked the old chain, failing with
'client.conversations.activities is not a function'. Update the mocks
to the flattened methods and their new signatures (conversationId is
now the first argument).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@lilyydu lilyydu added this pull request to the merge queue Jul 9, 2026
Merged via the queue into main with commit 2382334 Jul 9, 2026
7 checks passed
@lilyydu lilyydu deleted the lilyydu/stream-updates branch July 9, 2026 18:22
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.

3 participants