Skip to content

Users/axsuarez/slack enchanced support#390

Draft
axelsrz wants to merge 2 commits into
mainfrom
users/axsuarez/slack-enchanced-support
Draft

Users/axsuarez/slack enchanced support#390
axelsrz wants to merge 2 commits into
mainfrom
users/axsuarez/slack-enchanced-support

Conversation

@axelsrz
Copy link
Copy Markdown
Member

@axelsrz axelsrz commented May 13, 2026

This pull request introduces the new microsoft-agents-hosting-slack Python package, including its initial implementation, packaging, and integration into the CI/CD pipelines. The changes add Slack-specific hosting and API support to the Microsoft Agents ecosystem, following the established patterns for other hosting/storage packages.

The most important changes are:

New Slack Hosting Package Implementation

  • Added the microsoft-agents-hosting-slack package, including core modules for Slack integration such as slack_agent_extension, Slack API models, helpers, and path navigation utilities. This provides the foundational code for Slack bot hosting and API interaction. [1] [2] [3] [4] [5] [6] [7] [8] [9]

Packaging and Licensing

  • Added LICENSE (MIT) and MANIFEST.in (including VERSION.txt) to the new package to ensure proper open source compliance and packaging. [1] [2]

CI/CD Integration

  • Updated both Azure DevOps (.azdo/ci-pr.yaml) and GitHub Actions (.github/workflows/python-package.yml) pipelines to build and install the new microsoft_agents_hosting_slack wheel, ensuring the Slack hosting package is built and tested alongside other components. [1] [2]

Copilot AI review requested due to automatic review settings May 13, 2026 21:52
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR adds initial Slack hosting support to the Microsoft Agents Python ecosystem by introducing a new microsoft-agents-hosting-slack package, plus accompanying tests, a runnable Slack sample, and CI pipeline integration to build/install the new wheel.

Changes:

  • Introduces microsoft-agents-hosting-slack with Slack Web API client, streaming helper (SlackStream), Slack channel-data models, and routing extension (SlackAgentExtension).
  • Adds a Slack agent sample and a new tests/hosting_slack suite covering the new package behaviors.
  • Updates GitHub Actions and Azure DevOps pipelines to install the Slack hosting wheel during CI runs.

Reviewed changes

Copilot reviewed 35 out of 37 changed files in this pull request and generated 8 comments.

Show a summary per file
File Description
tests/hosting_slack/test_slack_stream.py Unit tests for SlackStream start/append/stop behavior and block parsing.
tests/hosting_slack/test_slack_response.py Unit tests for SlackResponse parsing and dot-path navigation.
tests/hosting_slack/test_slack_helpers.py Unit tests for Slack encode/decode and conversation-id helpers.
tests/hosting_slack/test_slack_channel_data.py Unit tests for SlackChannelData, Events envelope/content models, and path navigation.
tests/hosting_slack/test_slack_api.py Unit tests for SlackApi request serialization and error handling.
tests/hosting_slack/test_slack_agent_extension.py Unit tests for Slack-scoped route registration and API delegation.
tests/hosting_slack/test_path_navigator.py Unit tests for the dot/bracket path navigator utility.
tests/hosting_slack/init.py Test package marker for Slack hosting tests.
test_samples/extensions/slack-agent/src/start_server.py Aiohttp server bootstrap for the Slack sample agent.
test_samples/extensions/slack-agent/src/main.py Slack sample entrypoint wiring logging + app startup.
test_samples/extensions/slack-agent/src/app.py Slack sample app configuration (storage, adapter, auth).
test_samples/extensions/slack-agent/src/agent.py Slack sample agent routes demonstrating Slack API + streaming + blocks.
test_samples/extensions/slack-agent/src/init.py Slack sample package marker.
test_samples/extensions/slack-agent/requirements.txt Slack sample dependencies.
test_samples/extensions/slack-agent/README.md Slack sample usage and behavior documentation.
test_samples/extensions/slack-agent/env.TEMPLATE Slack sample environment template for Bot Service credentials.
libraries/microsoft-agents-hosting-slack/setup.py Slack hosting package setup (version + dependencies).
libraries/microsoft-agents-hosting-slack/readme.md Slack hosting package README for PyPI/docs.
libraries/microsoft-agents-hosting-slack/pyproject.toml Slack hosting package metadata and build configuration.
libraries/microsoft-agents-hosting-slack/microsoft_agents/hosting/slack/slack_helpers.py Slack formatting helpers + conversation-id parsing utilities.
libraries/microsoft-agents-hosting-slack/microsoft_agents/hosting/slack/slack_agent_extension.py Slack-specific route registration + helpers (call, create_stream).
libraries/microsoft-agents-hosting-slack/microsoft_agents/hosting/slack/api/slack_stream.py SlackStream implementation for chat.*Stream calls.
libraries/microsoft-agents-hosting-slack/microsoft_agents/hosting/slack/api/slack_response.py SlackResponse model + SlackResponseException.
libraries/microsoft-agents-hosting-slack/microsoft_agents/hosting/slack/api/slack_model.py Base SlackModel with dot-path get / try_get.
libraries/microsoft-agents-hosting-slack/microsoft_agents/hosting/slack/api/slack_channel_data.py SlackChannelData model bridging Bot Service Slack channelData.
libraries/microsoft-agents-hosting-slack/microsoft_agents/hosting/slack/api/slack_api.py SlackApi aiohttp client for Slack Web API calls.
libraries/microsoft-agents-hosting-slack/microsoft_agents/hosting/slack/api/event_envelope.py EventEnvelope model (outer Events API callback).
libraries/microsoft-agents-hosting-slack/microsoft_agents/hosting/slack/api/event_content.py EventContent model (inner event payload).
libraries/microsoft-agents-hosting-slack/microsoft_agents/hosting/slack/api/chunks.py Pydantic models for streaming chunk payload shapes.
libraries/microsoft-agents-hosting-slack/microsoft_agents/hosting/slack/api/action_payload.py ActionPayload model for interactive/block action callbacks.
libraries/microsoft-agents-hosting-slack/microsoft_agents/hosting/slack/api/init.py Public API exports for Slack hosting package.
libraries/microsoft-agents-hosting-slack/microsoft_agents/hosting/slack/_path_navigator.py Dot/bracket path navigation helper used by Slack models.
libraries/microsoft-agents-hosting-slack/microsoft_agents/hosting/slack/init.py Top-level Slack hosting exports.
libraries/microsoft-agents-hosting-slack/MANIFEST.in Ensures VERSION.txt is included in source distributions.
libraries/microsoft-agents-hosting-slack/LICENSE MIT license file for the new package.
.github/workflows/python-package.yml CI: installs the Slack hosting wheel during test runs.
.azdo/ci-pr.yaml Azure DevOps CI: installs the Slack hosting wheel during test runs.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +32 to +38
@property
def channel(self) -> Optional[str]:
"""Slack channel id, sourced from the envelope or action payload."""
if self.envelope is not None:
return self.envelope.get("event.channel")
if self.payload is not None:
return self.payload.get("channel")
if isinstance(value, dict):
return {k: _strip_nones(v) for k, v in value.items() if v is not None}
if isinstance(value, list):
return [_strip_nones(v) for v in value]
Comment on lines +86 to +98
if not chunks:
return self

await self._slack_api.call(
"chat.appendStream",
{
"channel": self._channel,
"ts": self._message_ts,
"thread_ts": self._thread_ts,
"chunks": [_chunk_to_dict(c) for c in chunks],
},
self._token,
)
Comment on lines +101 to +151
async def stop(
self,
chunks: Optional[Sequence[BaseModel]] = None,
blocks: Union[str, Sequence[Any], dict, None] = None,
) -> None:
"""Stop the active stream, optionally finalizing with chunks and/or
Block Kit blocks.

``blocks`` may be a JSON-array string, a JSON-object string containing
a ``"blocks"`` array, a Python list of block dicts, or a dict with a
top-level ``"blocks"`` key.

See https://docs.slack.dev/reference/methods/chat.stopStream
"""
if not self._message_ts:
return

resolved_blocks = self._resolve_blocks(blocks)
resolved_chunks = (
[_chunk_to_dict(c) for c in chunks] if chunks is not None else None
)

body: dict[str, Any] = {
"channel": self._channel,
"ts": self._message_ts,
"thread_ts": self._thread_ts,
}
if resolved_chunks is not None:
body["chunks"] = resolved_chunks
if resolved_blocks is not None:
body["blocks"] = resolved_blocks

await self._slack_api.call("chat.stopStream", body, self._token)

@staticmethod
def _resolve_blocks(blocks: Any) -> Optional[list[Any]]:
if blocks is None:
return None
if isinstance(blocks, str):
try:
parsed = json.loads(blocks)
except json.JSONDecodeError as exc:
raise ValueError("blocks string is not valid JSON") from exc
return SlackStream._resolve_blocks(parsed)
if isinstance(blocks, dict):
if "blocks" not in blocks or not isinstance(blocks["blocks"], list):
raise ValueError("blocks object must contain a 'blocks' array property")
return blocks["blocks"]
if isinstance(blocks, list):
return blocks
raise ValueError(
Comment on lines +101 to +122
async def create_stream(
self,
turn_context: TurnContext,
thread_ts: Optional[str] = None,
) -> SlackStream:
"""Create and start a :class:`SlackStream` for the current Slack thread."""
channel_data = SlackChannelData.from_activity(turn_context.activity)
if channel_data.envelope is None:
raise ValueError(
"create_stream requires a Slack event envelope on the activity"
)
resolved_thread_ts = thread_ts or channel_data.envelope.get("event.ts")
api = self._slack_api
if turn_context.has(_SLACK_API_SERVICE_KEY):
api = turn_context.get(_SLACK_API_SERVICE_KEY) # type: ignore[assignment]
stream = SlackStream(
api,
channel_data.envelope.get("event.channel"),
resolved_thread_ts,
channel_data.api_token or "",
)
return await stream.start()
"""Remap caller-supplied path before navigation. Default: identity."""
return path

def get(self, path: str, default: Optional[T] = None, type_: Type[T] = None) -> Any:
app["agent_app"] = agent_application
app["adapter"] = agent_application.adapter

run_app(app, host="localhost", port=environ.get("PORT", 3978))
Comment on lines +8 to +10
import json
from contextlib import asynccontextmanager
from typing import Any
@MattB-msft MattB-msft linked an issue May 14, 2026 that may be closed by this pull request
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.

Add Slack extension

2 participants