Skip to content

feat(agents): add Microsoft Teams bridge integration#2433

Merged
birdayz merged 6 commits into
masterfrom
jb/teams-bridge-integration
May 11, 2026
Merged

feat(agents): add Microsoft Teams bridge integration#2433
birdayz merged 6 commits into
masterfrom
jb/teams-bridge-integration

Conversation

@birdayz
Copy link
Copy Markdown
Contributor

@birdayz birdayz commented May 6, 2026

What

Add Microsoft Teams integration to the AI Agent system. New proto message, generated code, and an Integrations tab on the agent detail page.

Why

Users need to connect AI agents to Microsoft Teams for conversational interactions through a Teams bot. This requires configuring Azure Bot credentials on the agent side so the Teams bridge knows how to authenticate.

Implementation details

Proto

New AIAgentTeamsBridge message with four fields: enabled, bot_app_id, bot_tenant_id, bot_app_secret_ref. The secret ref uses the same ${secrets.NAME} template pattern as the existing provider API keys.

Added as teams_bridge to AIAgent (field 18), AIAgentCreate (field 15), and AIAgentUpdate (field 15).

UI

New Integrations tab on the agent detail page, positioned between Configuration and A2A. Contains a Microsoft Teams section with:

  • Enable/disable toggle
  • Application (client) ID and Tenant ID text fields
  • Client Secret via the existing SecretSelector component (scoped to AI_AGENT)
  • Read-only messaging endpoint URL (derived from agent URL), shown only when the integration is enabled and saved -- for pasting into Azure Bot registration

Update flow uses field masks targeting only teams_bridge.* fields, so it won't interfere with other agent configuration.

References

CRD struct: AIAgentTeamsBridge in cloudv2

birdayz added 2 commits May 6, 2026 16:36
Add AIAgentTeamsBridge proto message and wire it into the console UI
as a new Integrations tab on the agent detail page. The tab exposes
enable toggle, bot app ID, tenant ID, and client secret fields.
When saved and enabled, the computed messaging endpoint URL is
displayed for Azure Bot registration.

Proto: AIAgentTeamsBridge message added to AIAgent (field 18),
AIAgentCreate (field 15), and AIAgentUpdate (field 15).
…ndpoint

The agent URL pattern is <id>.ai-agents.<cluster>.clusters.ign.rdpa.co.
The old code only stripped the first subdomain, leaving ai-agents in place.
Now we find the ai-agents segment and take everything after it as the
cluster domain.
@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 6, 2026

The latest Buf updates on your PR. Results from workflow Buf CI / validate (pull_request).

BuildFormatLintBreakingUpdated (UTC)
✅ passed✅ passed✅ passed✅ passedMay 11, 2026, 3:17 PM

Add optional OUTPUT_ONLY teams_bridge_endpoint field to AIAgent.
The bridge controller populates this when it starts serving, so
console just displays it -- no URL gymnastics needed.
Copy link
Copy Markdown
Contributor

@malinskibeniamin malinskibeniamin left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems a bit specific to put teams bridge in AI agent service but otherwise LGTM

The bridge expects a bare key (e.g. TEAMS_BOT_SECRET) that it
resolves directly from the secret store. The ${secrets.} template
pattern is for interpolation in deployment env vars, not for ref
fields read by external services.

Removes extractSecretName regex and SECRET_TEMPLATE_REGEX. Proto
validation updated to accept bare UPPER_SNAKE_CASE keys.
@birdayz
Copy link
Copy Markdown
Contributor Author

birdayz commented May 7, 2026

Seems a bit specific to put teams bridge in AI agent service but otherwise LGTM

yeah that's fair. as per the design, it is a per agent configuration. Other systems, e.g. copilot studio do it the same way

bot_app_secret_ref validation changed from ${secrets.X} pattern
to bare UPPER_SNAKE_CASE key. Regenerate pb.go and pb.ts.
@birdayz birdayz force-pushed the jb/teams-bridge-integration branch from 72affdb to 1dc0b31 Compare May 11, 2026 14:48
@birdayz
Copy link
Copy Markdown
Contributor Author

birdayz commented May 11, 2026

@claude review plz

@birdayz birdayz marked this pull request as ready for review May 11, 2026 14:53
@claude
Copy link
Copy Markdown

claude Bot commented May 11, 2026

Claude finished @birdayz's task in 2m 36s —— View job


Claude PR Review

  • Read CLAUDE.md for repo conventions
  • Review proto changes
  • Review new Integrations tab UI
  • Review agent details page changes
  • Review feature flag gating

Overall this looks clean and well-scoped: a new proto message, generated code, and a single new tab gated behind a feature flag. The field-mask update flow is correctly scoped to teams_bridge.*. A few notes below — mostly polish, plus a couple of items worth confirming.

🟡 Notable

1. PR description is out of date re: secret formatproto/redpanda/api/dataplane/v1alpha3/ai_agent.proto:310-313

The PR description says "The secret ref uses the same ${secrets.NAME} template pattern as the existing provider API keys." That was true originally, but commit f0595e0 switched bot_app_secret_ref to a bare secret key:

string bot_app_secret_ref = 4 [
  (buf.validate.field).ignore = IGNORE_IF_ZERO_VALUE,
  (buf.validate.field).string.pattern = "^[A-Za-z_][A-Za-z0-9_]*$"
];

vs. provider API keys which still require ^\$\{secrets\.NAME\}$. This is intentional (matches the controller-side expectation), but the divergence from every other secret reference field in this same proto is worth a brief comment on the field, or at least an updated PR description so future readers don't get confused. Fix this →

2. No client-side validation when enabling the bridgefrontend/src/components/pages/agents/details/ai-agent-integrations-tab.tsx:89-131

handleSave will happily POST enabled: true with empty botAppId / botTenantId / botAppSecretName. The proto validators allow this (all four fields have IGNORE_IF_ZERO_VALUE or no required), so the server accepts the update — but the Teams bridge will then fail to start at runtime with no UI feedback. Worth either (a) disabling Save when enabled && (!botAppId || !botTenantId || !botAppSecretName), or (b) showing inline field errors.

3. Input is misused for inputs that should be GUIDsfrontend/src/components/pages/agents/details/ai-agent-integrations-tab.tsx:188-219

botAppId and botTenantId are Azure GUIDs but have no validation or pattern. Given that frontend/AGENTS.md calls for react-hook-form + Zod for forms, this tab is currently using bare Input + local useState. Not a blocker for this PR (the configuration tab next door has the same shape), but if you want this to land in the "modern" style it should be a react-hook-form + Zod form. See the form-refactorer skill.

🟢 Minor

4. Read-only display boxes use hardcoded grayai-agent-integrations-tab.tsx:199, 215, 237

<div className="flex h-10 items-center rounded-md border border-gray-200 bg-gray-50 px-3 py-2">

These won't adapt to dark mode. The same pattern appears in ai-agent-configuration-tab.tsx:953,967, so it's pre-existing — but consider border-border bg-muted or using a disabled <Input readOnly> for better dark-mode + a11y. Not new in this PR.

5. editedState/isEditing are two booleans for one state machine — lines 60-61

const [isEditing, setIsEditing] = useState(false);
const [editedState, setEditedState] = useState<TeamsBridgeState | null>(null);

These move in lockstep (Edit button sets the first, Cancel/Save reset both). One state — editedState: TeamsBridgeState | null — is sufficient (isEditing === editedState !== null). Currently fragile because nothing forces the invariant.

6. // biome-ignore lint/complexity/noExcessiveCognitiveComplexity — line 53

The biome-ignore is a smell; the component renders two parallel UIs (view vs. edit) per row. A ViewField / EditField split or a single <Field readOnly={!isEditing} /> would let this lint pass naturally. Low priority.

7. Edit-mode state is lost on tab switch — by design, the component unmounts when the user navigates to another tab, dropping unsaved edits silently. The Configuration tab next door behaves the same way, so this is consistent — flagging in case you want a beforeunload/dirty-state prompt eventually.

8. Feature flag gate is duplicatedai-agent-details-page.tsx:96, 125

isFeatureFlagEnabled('enableTeamsBridge') is evaluated twice. Tiny — extract to a local const showTeamsIntegrations = isFeatureFlagEnabled(...) so the two sites can't drift.

✅ Good

  • Field mask scoped to teams_bridge.* cleanly avoids interfering with other agent fields — nice.
  • Reading teams_bridge_endpoint from server status rather than deriving it client-side (per commit 4376678) is the right call; the URL stripping logic that was being replaced was fragile.
  • Boolean(agent.teamsBridgeEndpoint) && (...) correctly guards the read-only endpoint display.
  • Feature-flag gating is correct: both the trigger and the content are gated, so a user can't deep-link into a hidden tab.
  • Proto change is backwards-compatible (new field numbers 18/19/15).

Summary

LGTM modulo the validation gap in (2) — that one is worth fixing before merge so users get feedback when their config is incomplete. (1) is a small PR-description / proto-comment update. Everything else is polish that could be follow-up.
• Branch: jb/teams-bridge-integration

@birdayz birdayz force-pushed the jb/teams-bridge-integration branch from 1dc0b31 to eaf1612 Compare May 11, 2026 15:01
@birdayz
Copy link
Copy Markdown
Contributor Author

birdayz commented May 11, 2026

@claude re review

@claude
Copy link
Copy Markdown

claude Bot commented May 11, 2026

Claude finished @birdayz's task in 1m 19s —— View job


Claude PR Re-review

  • Check latest state of PR vs. previous review
  • Re-verify proto changes
  • Re-verify integrations tab UI
  • Re-verify feature flag gating
  • Post updated review

Re-reviewed at eaf1612. Most actionable items from the previous review are addressed. Quick status:

✅ Addressed since prior review

  • Client-side validation when enabling the bridgeai-agent-integrations-tab.tsx:89-91 now disables Save when enabled && (!botAppId || !botTenantId || !botAppSecretName). 👍
  • Read-only display dark-mode friendlinessai-agent-integrations-tab.tsx:201, 217, 239 now use border-border bg-muted instead of hardcoded border-gray-200 bg-gray-50. 👍
  • Two-boolean state machineai-agent-integrations-tab.tsx:60-61 collapsed into a single editedState: TeamsBridgeState | null with isEditing derived. 👍
  • Duplicated feature-flag checkai-agent-details-page.tsx:84 extracts const showTeamsBridge = isFeatureFlagEnabled('enableTeamsBridge') and reuses it at lines 98 and 127. 👍

🟡 Still open

1. Proto comment for bare-secret-key divergenceproto/redpanda/api/dataplane/v1alpha3/ai_agent.proto:305-314

The AIAgentTeamsBridge message and its bot_app_secret_ref field still have no doc comments. bot_app_secret_ref uses a bare-identifier pattern (^[A-Za-z_][A-Za-z0-9_]*$), unlike every other secret reference in this same proto which requires ^\$\{secrets\.NAME\}$. Worth a one-line comment so a future reader doesn't try to "fix the inconsistency." Same goes for the other three fields — they're undocumented compared to the rest of the file.

Fix this →

🟢 Minor / non-blockers (unchanged from prior review)

  • GUIDs validated only by placeholderai-agent-integrations-tab.tsx:194-219: botAppId / botTenantId accept any string. frontend/AGENTS.md calls for react-hook-form + Zod (the form-refactorer skill fits). Sibling Configuration tab has the same shape, so this is consistent — fine as follow-up.
  • biome-ignore noExcessiveCognitiveComplexity — line 53. A <Field readOnly={!isEditing} /> split would let the lint pass naturally; low priority.
  • Edit-mode state dropped on tab switch — by design, same as sibling Configuration tab. Flagging only as a future-UX consideration.

Summary

LGTM to merge. The proto comment in (1) is the only thing I'd still nudge on, and it's small enough to be a follow-up if needed. The validation, state, theming, and feature-flag cleanups all look good.
• Branch: jb/teams-bridge-integration

@birdayz birdayz force-pushed the jb/teams-bridge-integration branch from eaf1612 to 997305f Compare May 11, 2026 15:13
Off by default. Cloud-ui pipes the LaunchDarkly value through
useConsoleFeatureFlags; without it the tab and its content are
not rendered.

Also: disable Save when enabled with incomplete fields, consolidate
isEditing into editedState, fix dark-mode classes on read-only fields,
document proto fields including bare-key divergence on bot_app_secret_ref.
@birdayz birdayz force-pushed the jb/teams-bridge-integration branch from 997305f to 32ed960 Compare May 11, 2026 15:17
@birdayz birdayz enabled auto-merge (squash) May 11, 2026 15:27
@birdayz birdayz merged commit 0614954 into master May 11, 2026
25 checks passed
@birdayz birdayz deleted the jb/teams-bridge-integration branch May 11, 2026 15:30
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.

2 participants