Skip to content

Preserve textFormat through HttpStream's intermediate and final activities - #762

Merged
Mehak Bindra (MehakBindra) merged 2 commits into
mainfrom
copilot/fix-httpstream-textformat
Aug 28, 2026
Merged

Preserve textFormat through HttpStream's intermediate and final activities#762
Mehak Bindra (MehakBindra) merged 2 commits into
mainfrom
copilot/fix-httpstream-textformat

Conversation

Copilot AI commented Aug 25, 2026

Copy link
Copy Markdown
Contributor

Summary

HttpStream rebuilds outbound activities on every flush/close and forwarded the last emitted message's attachments, entities, suggested actions, and channelData — but not textFormat. As a result, stream.emit({ text, textFormat: 'extendedmarkdown' }) was accepted but dropped on every intermediate typing chunk and on the final message, so streamed Extended Markdown rendered as plain markdown.

Fix

  • Model textFormat on ITypingActivity / ITypingActivityInput / TypingActivityInput with a withTextFormat() builder (mirroring MessageActivityInput).
  • HttpStream reads the last emitted message's textFormat (same "last message wins" state as attachments/entities/etc.) and applies it to the cumulative typing chunks and informative updates in flush(), the final message in close(), and the timeout fallback in sendFinal().

Testing

  • Unit tests in packages/api/src/activities/typing.spec.ts and packages/apps/src/http/http-stream.spec.ts covering textFormat on typing activities and propagation across intermediate chunks, the final message, and the timeout fallback.
  • Validated live in Teams: with the fix, streamed task lists and strikethrough render correctly; without it they fall back to plain markdown.

Note on C#

This repo only contains the TypeScript SDK; the .NET streaming abstraction is out of scope here.

…ities

HttpStream rebuilds outbound wire activities (TypingActivityInput chunks and
the final MessageActivityInput) from scratch each flush/close cycle. It
already carries forward attachments, entities, suggested actions, and
channelData from the last emitted message ("last message wins"), but did not
carry forward textFormat, so `stream.emit({ type: "message", text,
textFormat: "extendedmarkdown" })` was silently dropped from every
intermediate typing chunk and the final message. The timeout/plain-final
fallback (sendFinal) had the same gap.

- Model textFormat on ITypingActivity/TypingActivityInput (previously only
  modeled on message activities), with a withTextFormat() builder method,
  mirroring MessageActivityInput.
- HttpStream now reads the last emitted message's textFormat off
  this.finalActivity (the existing last-message-wins state) and applies it to
  every cumulative typing chunk, informative typing updates, the normal final
  message, and the timed-out plain-final fallback.
- Add unit tests covering textFormat propagation across intermediate chunks,
  the final message, and the timeout fallback.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot AI requested a lite review from Copilot and removed request for Copilot August 25, 2026 21:11
@corinagum

Corina (corinagum) commented Aug 26, 2026

Copy link
Copy Markdown
Collaborator

Kavin (@singhk97) Do we have confirmation from service team that typing activities are supposed to support textFormat? This is an alarming API change if so. Confirmed by Lily: https://learn.microsoft.com/en-us/microsoftteams/platform/bots/streaming-ux?tabs=csharp&pivots=teams-sdk-csharp#stream-message-through-rest-api

@singhk97

Copy link
Copy Markdown
Collaborator

Before fix:

before-fix.mp4

After fix:

after-fix.mp4
…mple

Adds an 'extended-markdown' trigger that streams a release-status update where each delta sets textFormat: 'extendedmarkdown', so task lists and strikethrough render while streaming. Also trims verbose textFormat comments per review feedback.

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

Copilot-Session: 0073e3f0-b7b4-464a-9bdf-30f97d8081ec
Copilot AI lite review requested due to automatic review settings August 27, 2026 19:53

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

This PR fixes a streaming regression where HttpStream rebuilt intermediate/final activities without carrying forward the last emitted message’s textFormat, causing streamed Extended Markdown to render as plain markdown. It extends typing activity models/builders to support textFormat, and ensures HttpStream propagates that value consistently across intermediate typing chunks, final close, and timeout fallback.

Changes:

  • Add textFormat support (and withTextFormat() builder) to typing activity interfaces and builders in @microsoft/teams.api.
  • Update HttpStream to apply the last emitted message’s textFormat to intermediate typing chunks, the final message sent on close(), and the sendFinal() timeout fallback.
  • Add unit tests covering typing textFormat modeling and end-to-end propagation through streaming behavior.

Reviewed changes

Copilot reviewed 6 out of 6 changed files in this pull request and generated no comments.

Show a summary per file
File Description
packages/apps/src/http/http-stream.ts Propagates finalActivity.textFormat into typing chunks and final/timeout message sends.
packages/apps/src/http/http-stream.spec.ts Adds tests verifying textFormat is retained for intermediate chunks, final message, and timeout fallback.
packages/api/src/activities/typing.ts Models textFormat on typing activities and adds withTextFormat() builders.
packages/api/src/activities/typing.spec.ts Adds unit tests for TypingActivityInput and TypingActivity textFormat behavior.
examples/stream/src/index.ts Extends the streaming example to demonstrate Extended Markdown deltas via textFormat: 'extendedmarkdown'.
examples/stream/README.md Documents how to trigger the Extended Markdown streaming demo.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

@MehakBindra
Mehak Bindra (MehakBindra) added this pull request to the merge queue Aug 28, 2026
Merged via the queue into main with commit 8ab63af Aug 28, 2026
10 checks passed
@MehakBindra
Mehak Bindra (MehakBindra) deleted the copilot/fix-httpstream-textformat branch August 28, 2026 23:05
Kavin (singhk97) added a commit to microsoft/teams.py that referenced this pull request Sep 1, 2026
…vities (#581)

## Summary

Ports
[microsoft/teams.ts#762](microsoft/teams.ts#762)
and its follow-up [#784](microsoft/teams.ts#784)
to the Python SDK.

On every `flush()`, `HttpStream` rebuilds the outbound typing chunk and
forwards the last emitted message's attachments, entities, suggested
actions, and channel data ("last message wins" via
`self._final_activity`) — but not `text_format`. So
`stream.emit(MessageActivityInput(text=...).with_text_format("extendedmarkdown"))`
was dropped from every intermediate chunk, and streamed Extended
Markdown (task lists, strikethrough) rendered as plain markdown until
the message closed.

The final message and timeout fallback (`_send_final`) already preserved
`text_format`, since they reuse the real `_final_activity` object rather
than rebuilding a copy (unlike TS). Only the streamed chunks needed
fixing.

## Changes

- Add `text_format` + a `with_text_format()` builder to `_TypingBase`
(shared by `TypingActivity` / `TypingActivityInput`), mirroring
`MessageActivityInput`.
- `HttpStream._flush()` applies the last emitted message's `text_format`
to the combined streamed-text chunk (last-message-wins).
- **Informative updates keep their own `text_format`.** `_flush()`
previously overwrote each informative update's format with
`_final_activity.text_format`; it now leaves the update's own value
intact (`_final_activity` isn't even set when informative updates are
sent).
- **`update()` gains an optional `text_format`.**
`StreamerProtocol.update` / `HttpStream.update` now accept
`text_format`; `None` → Teams default (`markdown`), an explicit value →
that format. Previously the only way to format an informative update was
to hand-build a typing activity and `emit()` it.
- `examples/stream`: refresh the `extended-markdown` scenario and demo
the new `update(text, "markdown")` overload.
- Unit tests for `text_format` on typing activities, its propagation
across streamed chunks / final message / timeout fallback,
informative-update format independence (red/green against the old
overwrite), and the `update()` overload.

## Testing

- `ruff format --check`, `ruff check`, `pyright`, `pytest packages`
(1107 passed) — all pass.
- Verified live in Teams: streaming a message with
`text_format="extendedmarkdown"` renders task lists and strikethrough on
each intermediate chunk.

_Created from a [Microsoft Teams
conversation](https://teams.microsoft.com/l/message/19%3AeXvmZeTKJ1Myy2_qfz4vp4-YjeIqCMJ4xF6poI2GxtU1%40thread.tacv2/1787691375435?tenantId=72f988bf-86f1-41af-91ab-2d7cd011db47&groupId=1314f851-c930-4caa-b3e0-dbe9b8fe2737&parentMessageId=1787691375435)._

---------

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Kavin Singh <kavinsingh@microsoft.com>
Co-authored-by: Kavin <115390646+singhk97@users.noreply.github.com>
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

5 participants