Skip to content

feat(msteams): inline Report chart images + Adaptive Card theming — reach display parity with google_chat #200

Description

@sipemu

Summary

The MS Teams adapter now has the full interactive round-trip (#155Button/Selection/Form → Adaptive Card actions, verified callback, in-place refresh). What remains before it is at full feature parity with google_chat is the one-way display richness: inline chart images and card theming. This is the Teams sibling of #154 (which did the same for google_chat) and is out of scope for #155 by design.

Current state (post-#155)

crates/triton-chat-msteams/src/surface_mapper.rs:

  • Component::Report is ignored. The text mapper drops it (Component::Report { .. } => {}) and the card path (render_card_content in lib.rs) only lifts interactive controls + Dashboard. So an agent that emits a rendered chart (e.g. peacock render_report) shows nothing on Teams.
  • Dashboard renders as a FactSet only (metric tiles). No chart image. Per architecture.md §8.7 this is arguably the intended native projection for Teams (ColumnSet/FactSet, skip the rasteriser), so the tiles are fine — but a genuinely rendered chart (a Report) still needs an Image element.
  • No card theming. By contrast google_chat's CardChrome pulls get_theme (logo, brand colour, header) and brands every card; Teams cards are unbranded.
  • No image-serving route. google_chat serves chart PNGs at a signed …/img/{token} route (serve_dashboard_png); msteams' router has only the /<name>/webhook route.

Proposed work

1. Inline Report → Adaptive Card Image.

  • Add report_from_result (mirror google_chat) to lift Component::Report { report_id, args }.
  • On a reply carrying a Report, dispatch render_report(report_id, args) through the upstream router with the resolved principal (identity + audit symmetry), take the returned PNG, and embed it in the card.
  • Teams Adaptive Cards render images by URL, so this needs the hosting story below (a signed …/img/{token} route serving the cached PNG), OR — if small enough — a data: URI Image (verify Teams' size limits before relying on it).

2. Signed image route.

  • Add a GET /<name>/img/{token} route that decodes a signed token and returns the cached PNG, mirroring google_chat's serve_dashboard_png (including the RENDER_REPORT_IMG_MARKER distinction between a dashboard-spec token and an upstream-rendered-PNG token, and the replay guard that a button/form token can't be replayed at …/img/).

3. Adaptive Card theming (get_theme → chrome).

  • Add a msteams CardChrome analogue: fetch get_theme from the report upstream and apply title/logo/brand colour to the Adaptive Card (card title/Image header, Action.Style: positive or a container style for brand colour — Adaptive Cards don't take arbitrary hex on buttons, so map brand → the closest native affordance).

4. (Optional) Dashboard-as-image.

  • If a Dashboard should ever render as a rasterised chart on Teams (rather than the native FactSet), reuse the same …/img/{token} path. Default remains the native FactSet per §8.7 — decide explicitly.

Out of scope

Acceptance criteria

  • A surface with a Report renders an Adaptive Card carrying the upstream-rendered chart image on Teams.
  • The chart image is served from a signed …/img/{token} route; a forged/replayed token is rejected.
  • get_theme chrome (title/logo/brand) is applied to interactive + report cards; unset theme = today's unbranded rendering.
  • Unit + no-mock integration tests: Report → image card shape; the image route round-trip against the real binary + a fake upstream that returns a PNG; a forged image-token rejection.

References

Activity

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions