Skip to content

feat(apps): integrate socket mode with app (PR 2 of 2) - #608

Merged
Lily Du (lilyydu) merged 9 commits into
mainfrom
lilyydu/socket-mode-app-integration
Sep 28, 2026
Merged

Lily Du (lilyydu) merged 9 commits into
mainfrom
lilyydu/socket-mode-app-integration

Conversation

@lilyydu

Copy link
Copy Markdown
Collaborator

Stacks on #604. Adds the public layer.

SocketModeAdapter implements the HttpServerAdapter protocol, so Socket Mode replaces the inbound transport instead of adding a second ingress. Enable with App(socket_mode=True) or pass SocketModeOptions.

Envelopes dispatch through the normal activity pipeline. The socket is authenticated at negotiate time, so the adapter synthesizes the token the pipeline expects; it stringifies to "" so it can never be forwarded. HTTP JWT validation is untouched.

Rejects a custom http_server_adapter and non-public clouds. Adds examples/socket.

@lilyydu Lily Du (lilyydu) changed the title connect socket mode to app feat(apps): integrate socket mode with app (PR 2 of 2) Sep 24, 2026
@lilyydu
Lily Du (lilyydu) requested a lite review from Copilot September 24, 2026 16:35

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.

Copilot review overview

🟡 Changes recommended

Unresolved lifecycle, endpoint, awaitable-handling, error-reporting, and reconnection findings remain.

Get a fresh assessment by requesting another Copilot review.

Review effort: Lite
Findings: 3 Medium severity

Open (3)
What changed in this PR

Adds public Socket Mode support to App, including transport integration, lifecycle handling, tests, and an example application.

Changes:

  • Adds SocketModeAdapter and SocketModeOptions.
  • Integrates Socket Mode as an alternative inbound transport.
  • Adds connection handoff behavior, tests, documentation, and a sample.

Review findings:

  • Moderate (2 votes): cancellation may leave transport resources running.
  • Moderate (3 votes): non-coroutine awaitables may not be awaited.
  • Moderate (2 votes): the default negotiate endpoint is currently unavailable.
  • Moderate (1 vote): pipeline errors may be reported twice.
  • Moderate (1 vote): reconnect events may be emitted without a delivery gap.
  • Nit (1 vote): re-export SocketModeEventType from the top-level package.
File Description
uv.lock Registers the new example workspace.
packages/​apps/​tests/​test_socket_mode_transport.py Tests connection rotation and handoff behavior.
packages/​apps/​tests/​test_socket_mode_adapter.py Tests adapter and App integration.
packages/​apps/​src/​microsoft_teams/​apps/​socket_mode/​transport.py Exposes negotiate URL construction.
packages/​apps/​src/​microsoft_teams/​apps/​socket_mode/​geo_socket.py Supports connection rotation and retirement.
packages/​apps/​src/​microsoft_teams/​apps/​socket_mode/​adapter.py Implements the public adapter and activity handling.
packages/​apps/​src/​microsoft_teams/​apps/​socket_mode/​__init__.py Exports Socket Mode types.
packages/​apps/​src/​microsoft_teams/​apps/​options.py Adds Socket Mode options.
packages/​apps/​src/​microsoft_teams/​apps/​app.py Integrates Socket Mode with App.
packages/​apps/​src/​microsoft_teams/​apps/​__init__.py Re-exports public APIs.
examples/​socket/​src/​main.py Adds a Socket Mode sample app.
examples/​socket/​README.md Documents the sample.
examples/​socket/​pyproject.toml Defines sample dependencies.

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

Comment thread packages/apps/src/microsoft_teams/apps/socket_mode/adapter.py
Comment thread packages/apps/src/microsoft_teams/apps/socket_mode/adapter.py Outdated
Comment thread packages/apps/src/microsoft_teams/apps/socket_mode/adapter.py Outdated
Base automatically changed from lilyydu/socket-mode-transport-foundation to main September 24, 2026 18:00
@lilyydu
Lily Du (lilyydu) force-pushed the lilyydu/socket-mode-app-integration branch from 7d739ef to ec6ff13 Compare September 24, 2026 18:22
Lily Du (lilyydu) and others added 2 commits September 24, 2026 11:23
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Handle asyncio.CancelledError during transport start.

Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
@AlxBit

Copy link
Copy Markdown
Contributor

This is exciting news for our Dataiku DSS Teams integration! Removing the inbound public endpoint/tunnel would simplify both development and deployment.
Today, we use a workaround in which an Azure Function validates and relays events through Azure Web PubSub to DSS: Dataiku DSS Teams integration documentation.
This looks closely related to the Teams feedback request for receiving callbacks over WebSockets without exposing a public endpoint:
https://feedbackportal.microsoft.com/feedback/idea/7c6bc28c-28b3-f011-aa44-7c1e5298a4a1
Is Socket Mode intended to address that request, at least for bot activities?

We’re particularly interested in understanding:

  • Multiple instances: If multiple teams.py application instances connect with the same Azure Bot Service / bot identity, how are activities routed between those instances and their geo connections? Can an activity be delivered to more than one instance, or redelivered during reconnect or token rotation?
  • Acknowledgement: The implementation appears to send the activity reply only after the handler completes:
    await self._send(serialize_completion(signalr_invocation_id, payload))
    Is there a service-side acknowledgement deadline, and is there a recommended pattern? Some of our handlers hand work off to asynchronous Dataiku tasks before the agent can reply, so guidance on whether to acknowledge quickly and process asynchronously versus await the work would help us integrate it reliably.

@lilyydu

Copy link
Copy Markdown
Collaborator Author

Hey Alex Bitar (@AlxBit) ! We're very glad to hear your team is interested in this feature!

As changes are across various backend services, to ensure reliability- we will be shipping this upcoming preview only for development bots. For your knowledge, we currently don't have a firm release date as we are rigorously testing internally.

Socket mode will be an alternative inbound delivery path for Teams agents and it will not require a publicly reachable HTTPS endpoint. However, existing outbound activity flows will remain HTTP-based.

In terms of multiple instances, each instance connects independently, with its own per-geo connections.
For the ack, there is a service-side reply budget, carried in the envelope’s deadlineMs field. Right now, it's about 25 seconds, but this may change in the future.

@lilyydu
Lily Du (lilyydu) merged commit bf80684 into main Sep 28, 2026
8 checks passed
@lilyydu
Lily Du (lilyydu) deleted the lilyydu/socket-mode-app-integration branch September 28, 2026 21:04
pull Bot pushed a commit to Mattlk13/teams.ts that referenced this pull request Sep 29, 2026
…nvelope metadata (microsoft#875)

Brings TS Socket Mode to parity with microsoft/teams.py#608 (commit
`95a0b0fc`). This re-lands the intent of microsoft#810, which merged into a
stacked branch and never reached `main`.

## Changes

### Stop retrying Socket Mode negotiate on 401/403
- `NegotiateError` carries the HTTP `statusCode` and an `isAuthError`
flag. `statusCode` is an optional third constructor argument, so
existing calls still compile.
- `NegotiateError` is now exported from `@microsoft/teams.apps`, so
`disconnected` listeners can inspect `statusCode` and `isAuthError`.
- The 401 and 403 error messages now say what to fix: the bot
credentials for a 401, the bot registration and Socket Mode access for a
403.
- **Startup:** a 401 or 403 fails `App.start()` right away instead of
retrying until the startup timeout runs out.
- **Reconnect or planned token rotation:** a 401 or 403 closes every
connection for that geo, including a predecessor still serving and any
sockets in their handoff window. The geo then stops reconnecting. Other
geos are unaffected.
- It logs one error saying delivery for the geo has stopped until the
app is restarted, without the usual "paused" warning.
  - It emits `disconnected` with the auth error and `terminal: true`.
- **Late rejections after `stop()`:** negotiate isn't abortable, so a
401 or 403 can arrive after `stop()`. That rejection is now ignored:
it's not logged and doesn't emit `disconnected`.
- Other failures (429, 5xx, ...) are still retried.

### `disconnected` event: new `terminal` field
- The payload is now `{ geo, error?, terminal }`.
- `terminal` is `true` when the geo has stopped for good after a 401 or
403, and `false` for an ordinary drop that will reconnect. So after a
network drop followed by an auth rejection, listeners see two events:
`terminal: false`, then `terminal: true`.
- `status` and `geoStatuses` are unchanged and report `disconnected` in
both cases.
- The `examples/socket` sample and its README now handle `terminal`.

### Parse Socket Mode envelope delivery metadata
- Adds `botKey`, `deadlineMs` and `headers` to `SocketActivityEnvelope`,
in camelCase or PascalCase.
- These are typed fields only: they are not validated at runtime, so
malformed values pass through like any other envelope field.
- Reply frames still carry the locally configured bot id.

## Testing
- `npm run build` and `eslint src/socket-mode` in `packages/apps`, plus
type-checking and lint for `examples/socket`
- `npx jest` in `packages/apps`: 49 suites, 873 tests passed
- New tests cover:
  - 401 and 403 at startup
- 401 and 403 on reconnect, including the `terminal` flag on each event
and that only the terminal error is logged
  - a 403 during a planned rotation with multiple geos
  - a 401 that arrives after `stop()` being ignored
  - 503 still being retried
  - `NegotiateError` being exported from the package root
  - envelopes carrying the new metadata fields are still dispatched
- Each new behavior test was checked red-green: it fails with the
behavior disabled and passes with it enabled.

---------

Co-authored-by: Teddy Arida-Moody <teddyam@Teddys-MacBook-Pro.local>
Co-authored-by: Copilot App <223556219+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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants