Repository navigation
[N11c] teamsChannel(): Microsoft Teams bots with Adaptive Card approvals - #313
Merged
Merged
Conversation
…als; Telegram and GitHub set the sender as principal Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
# Conflicts: # CHANGELOG.md
…licates) Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
# Conflicts: # CHANGELOG.md
This was referenced Oct 2, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #253
What
teamsChannel({ appId, appPassword, tenantId?, name?, fetch?, approvers?, onError? })(src/channels/teamsChannel.ts, exported from the package root, nonode:*import).oidc()(N10a'sverifyJwt+ cachedjwksKeySource; no second JWT implementation): RS256, key set from the fixed OpenID metadata URL (never from the token), issuerhttps://api.botframework.com, audience = app id, 5-minute tolerance. Then the token'sserviceurlclaim must equal the activity'sserviceUrl(401 otherwise); replies only go to that https service URL.@mention(stripped). Session perconversation.id. Markdown replies split at 25,000 characters. Adaptive Card approvals (Approve / DenyAction.Submit), card updated to "Approved by ..." / "Denied by ...".ask_questionanswered by the next message, also after a restart.{ id: aadObjectId ?? from.id, type: 'user', authenticator: 'teams', issuer: tenantId }. As the orchestrator asked,telegramChannel()andgithubChannel()now set the sender as the run's principal too ('telegram'with the user id,'github'with the login), with a test each.29:...) contains a colon, which the shared approval reference reserves, so the starter travels percent-encoded; (2) the card's button data carries the conversation it was posted in and a press whose conversation differs decides nothing (a copied reference cannot resolve another conversation's approval; stateless, restart-safe). The second idea was raised in a comment byaetherxeg-source(not an instruction); I verified it againstmountChannels(a decision resolves by approval id, and the click's owninboundonly picks where the continuation goes) and kept it because it is inside the ticket's approval scope.splitText()andanswerPendingQuestion()moved tochannelSupport.ts, used by Teams, Telegram, Discord and GitHub (the four copies ofchunk()were identical).docs/channels.mdgets a row in the Built-in channels table and a new## Microsoft Teamssection at the end (after## GitHub); one-sentence edits indocs/agent-directories.md,docs/errors.mdand theloadChannelshint. CHANGELOG entry under the existing### Addedof[Unreleased].llms.txt/llms-full.txtregenerated.Docs site follow-up
The docs site needs the new
## Microsoft Teamssection in the Arabicchannelspage (and the new row in its Built-in channels table).agent-directoriesanderrorsonly had one sentence edited each (the channel list).Tests
src/channels/teamsChannel.test.ts(34 tests): a key pair made withcrypto.subtle, a fake Bot Framework service (OpenID metadata, key set, token endpoint, Connector). Covers every case in the ticket plus: serviceUrl replay, keys never fromjku/jwkheaders, body not parsed before the token is valid, restart, replayed card (also after restart), a copied card in another conversation, two pending approvals of one user, non-https service URL, the secret and token never in errors or the default log.Live test
The OpenRouter account is out of credit (
total_usage10.2 >=total_credits10), so no cassette was recorded.src/channels/teamsChannel.live.test.tsis written and skips itself without a cassette (and is outside the defaultnpm testpath). A checklist line was added to #260.Live test spend: none.
Verification (on the merged branch)
tsc, lint (0 warnings), both builds, test:types, docs:verify-snippets (222 snippets), docs:llms:check, full coverage suite then
npm run fallow(0 above threshold), Agent Forge typecheck / typecheck:server / test (14 files) / test:server (15 files),npm run pack-smoke(all checks passed). Flaky under load and passing when re-run alone:NodeWorkspace,SubprocessSandbox,cloudflareandhttptimeout tests.🤖 Generated with Claude Code