[codex] Add teamsbot ingress service#665
Conversation
6f537f8 to
f0067cf
Compare
|
@neokry Thank you for the diff! Was this tested with an actual Teams production or deployed to somewhere? Since internally we don't use Teams we would need to get a full E2E validation and a champion to carry this integration moving forward. |
Hey no problem! Yes I have tested with my own teams and have it working. I could send a screen recording or add you to the teams deployment for a test. What do you think is the best path to move foward? If screen recording works what would you need to see included for full e2e verification? |
Sure! Some videos of the critical flows like triggering a model answer by tagging the bot would be sufficient. I'll review the rest in the meantime and may have comments. |
goksu
left a comment
There was a problem hiding this comment.
Architecture concern on packages/api-client/src/session-transport.ts.
| @@ -0,0 +1,318 @@ | |||
| export type FetchLike = (input: RequestInfo | URL, init?: RequestInit) => Promise<Response>; | |||
There was a problem hiding this comment.
This seems like a full new module introducing some sort of "shared" layer between renderers. Although in idea this may seem like a "code-cleanup" it actually likely will regress Slack and Discord bots and how they handle rendering.
On Centaur architecture we are trying to approach rendering with 3 layers:
- Common Rust API / codex transport server: fully typechecked
- Rendering layer converting server types to events that can be consumed by clients
- Each client building its own session handling (Slackbot, Discordbot)
We should instead of trying to do this just build any specific handler needs into Teamsbot.
| for (let attempt = 0; ; attempt += 1) { | ||
| const deferredCount = await teamsbot.recoverRenderObligations(); | ||
| if (deferredCount === 0) { | ||
| return; |
There was a problem hiding this comment.
This recovery loop exits permanently once a scan has no deferred obligations. If a live Teams render later stores a new renderObligation, nothing schedules another pass, so recovery can be stranded until pod restart. This should be periodic/long-running or rescheduled when an obligation is indexed.
|
Thanks for reviewing the changes @goksu I made some new commits that should handle those issues you brought up. Here is a video of teamsbot usage also: Screen.Recording.2026-06-22.at.11.43.52.AM.mov |
goksu
left a comment
There was a problem hiding this comment.
Thanks for the updates @neokry! We are getting close!
Have you looked into integrating the existing Chat SDK interface instead of going with an interface that is "hand-rolled" for Teams here? Was there any blockers you ran into? If not it might actually drop the number of lines in this diff significantly and allow a better chance to review and keep up to date in the future.
| | Discord channel | `discord-channel-<guild-id>-<channel-id>` | | ||
| | Teams user | `msteams-user-<tenant-id>-<user-id>` | | ||
| | Teams channel | `msteams-channel-<tenant-id>-<team-id>-<conversation-id>` | | ||
| | Teams chat fallback | `msteams-chat-<tenant-id>-<conversation-id>` | |
There was a problem hiding this comment.
Seems odd to be having a fallback selector here? What is the use case?
| falls back to the requesting user's grants. DMs and one-person runs normally use | ||
| the user principal directly. | ||
| Channel grants are shared by everyone in that channel or conversation. DMs, | ||
| Teams personal chats, and one-person runs normally use the user principal |
There was a problem hiding this comment.
This update specific to how Teams work seems unnecessary detail.
| @@ -60,7 +63,7 @@ impl PrincipalRef { | |||
| /// rename. | |||
| pub fn derive_principal( | |||
There was a problem hiding this comment.
@mslipper — If we proceed this change it seems like it will be the first instance of the proxy being extended for a different renderer. There are bunch of Slack specific functionality here. Have you thought about how to introduce multi-client to this surface? Would it be another layer above proxy that specifically handles client specific author / channel parsing or does this change here make sense for the time being?
Summary
Adds a Microsoft Teams ingress service at parity with the existing Slack and Discord services. The Teamsbot receives Bot Framework activities, gates them by Teams/team/channel/tenant policy, forwards session messages to api-rs, renders streamed session output back to Teams, and persists render obligations for recovery after crashes or delivery failures.
Also adds shared session stream parsing in
@centaur/api-clientso Teams, Slack, and Discord consume session events consistently, plus Teams principal derivation for iron-control permissioning.What Changed
services/teamsbotwith Teams SDK ingress, reply sinks, attachment hydration, thread/context serialization, Postgres-backed state, render recovery, local simulation, tests, Dockerfile, and README.packages/api-clientand moved Slack/Discord SSE parsing onto the shared parser.Validation
pnpm --filter teamsbot check:typesbun test test/teamsbot.test.ts/readyreturned 200 while running through the dev server.Notes