From 729a659a3c8a949dc9c3b6a2ffe1ae3a49f33bd7 Mon Sep 17 00:00:00 2001 From: Dilee Date: Thu, 7 May 2026 17:53:38 +0300 Subject: [PATCH] fix(teams-pipeline): add skill asset and fix async test env --- .../teams-meeting-pipeline/SKILL.md | 50 +++++++++++++++++++ tests/gateway/test_teams.py | 28 +++++------ 2 files changed, 64 insertions(+), 14 deletions(-) create mode 100644 skills/productivity/teams-meeting-pipeline/SKILL.md diff --git a/skills/productivity/teams-meeting-pipeline/SKILL.md b/skills/productivity/teams-meeting-pipeline/SKILL.md new file mode 100644 index 0000000000..9dce9b94e0 --- /dev/null +++ b/skills/productivity/teams-meeting-pipeline/SKILL.md @@ -0,0 +1,50 @@ +--- +name: teams-meeting-pipeline +description: "Operate the Teams meeting summary pipeline via Hermes CLI." +version: 1.0.0 +author: Hermes Agent +license: MIT +prerequisites: + env_vars: [MSGRAPH_TENANT_ID, MSGRAPH_CLIENT_ID, MSGRAPH_CLIENT_SECRET] + commands: [hermes] +metadata: + hermes: + tags: [Teams, Microsoft Graph, Meetings, Productivity, Operations] +--- + +# Teams Meeting Pipeline + +Use this skill when the user asks to summarize a Teams meeting, extract action items, inspect pipeline status, replay a stored job, or validate Microsoft Graph meeting-ingest setup. + +Prefer the Hermes CLI over ad hoc scripts. Route operator actions through the terminal tool with `hermes teams-pipeline ...`. + +## When to use + +- "Teams meeting ozetle" +- "action item cikar" +- "toplanti notu" +- "pipeline durumu" +- "replay job" + +## Required environment + +Set these in `~/.hermes/.env` before using the pipeline: + +```bash +MSGRAPH_TENANT_ID=... +MSGRAPH_CLIENT_ID=... +MSGRAPH_CLIENT_SECRET=... +``` + +## Common commands + +```bash +hermes teams-pipeline list +hermes teams-pipeline show +hermes teams-pipeline replay +hermes teams-pipeline fetch --meeting-id +hermes teams-pipeline token-health +hermes teams-pipeline maintain-subscriptions +``` + +Start with `validate`, `list`, or `show` when the user asks for status. Use `replay` only when they explicitly want to rerun a stored job. Use `fetch` for dry-run artifact checks before changing pipeline config. diff --git a/tests/gateway/test_teams.py b/tests/gateway/test_teams.py index bd6add2107..c8730f7684 100644 --- a/tests/gateway/test_teams.py +++ b/tests/gateway/test_teams.py @@ -360,7 +360,7 @@ class TestTeamsInteractiveSetup: assert "TEAMS_TENANT_ID=tenant-id" in env_text class TestTeamsConnect: - @pytest.mark.asyncio + @pytest.mark.anyio async def test_connect_fails_without_sdk(self, monkeypatch): monkeypatch.setattr(_teams_mod, "TEAMS_SDK_AVAILABLE", False) adapter = TeamsAdapter(_make_config( @@ -369,7 +369,7 @@ class TestTeamsConnect: result = await adapter.connect() assert result is False - @pytest.mark.asyncio + @pytest.mark.anyio async def test_connect_fails_without_credentials(self): adapter = TeamsAdapter(_make_config()) adapter._client_id = "" @@ -378,7 +378,7 @@ class TestTeamsConnect: result = await adapter.connect() assert result is False - @pytest.mark.asyncio + @pytest.mark.anyio async def test_disconnect_cleans_up(self): adapter = TeamsAdapter(_make_config( client_id="id", client_secret="secret", tenant_id="tenant", @@ -400,7 +400,7 @@ class TestTeamsConnect: # --------------------------------------------------------------------------- class TestTeamsSend: - @pytest.mark.asyncio + @pytest.mark.anyio async def test_send_returns_error_without_app(self): adapter = TeamsAdapter(_make_config( client_id="id", client_secret="secret", tenant_id="tenant", @@ -410,7 +410,7 @@ class TestTeamsSend: assert result.success is False assert "not initialized" in result.error - @pytest.mark.asyncio + @pytest.mark.anyio async def test_send_calls_app_send(self): adapter = TeamsAdapter(_make_config( client_id="id", client_secret="secret", tenant_id="tenant", @@ -426,7 +426,7 @@ class TestTeamsSend: assert result.message_id == "msg-123" mock_app.send.assert_awaited_once_with("conv-id", "Hello") - @pytest.mark.asyncio + @pytest.mark.anyio async def test_send_handles_error(self): adapter = TeamsAdapter(_make_config( client_id="id", client_secret="secret", tenant_id="tenant", @@ -439,7 +439,7 @@ class TestTeamsSend: assert result.success is False assert "Network error" in result.error - @pytest.mark.asyncio + @pytest.mark.anyio async def test_send_typing(self): adapter = TeamsAdapter(_make_config( client_id="id", client_secret="secret", tenant_id="tenant", @@ -594,7 +594,7 @@ class TestTeamsMessageHandling: ctx.activity = activity return ctx - @pytest.mark.asyncio + @pytest.mark.anyio async def test_personal_message_creates_dm_event(self): adapter = TeamsAdapter(_make_config( client_id="bot-id", client_secret="secret", tenant_id="tenant", @@ -610,7 +610,7 @@ class TestTeamsMessageHandling: event = adapter.handle_message.call_args[0][0] assert event.source.chat_type == "dm" - @pytest.mark.asyncio + @pytest.mark.anyio async def test_group_message_creates_group_event(self): adapter = TeamsAdapter(_make_config( client_id="bot-id", client_secret="secret", tenant_id="tenant", @@ -625,7 +625,7 @@ class TestTeamsMessageHandling: event = adapter.handle_message.call_args[0][0] assert event.source.chat_type == "group" - @pytest.mark.asyncio + @pytest.mark.anyio async def test_channel_message_creates_channel_event(self): adapter = TeamsAdapter(_make_config( client_id="bot-id", client_secret="secret", tenant_id="tenant", @@ -640,7 +640,7 @@ class TestTeamsMessageHandling: event = adapter.handle_message.call_args[0][0] assert event.source.chat_type == "channel" - @pytest.mark.asyncio + @pytest.mark.anyio async def test_user_id_uses_aad_object_id(self): adapter = TeamsAdapter(_make_config( client_id="bot-id", client_secret="secret", tenant_id="tenant", @@ -655,7 +655,7 @@ class TestTeamsMessageHandling: event = adapter.handle_message.call_args[0][0] assert event.source.user_id == "aad-stable-id" - @pytest.mark.asyncio + @pytest.mark.anyio async def test_self_message_filtered(self): adapter = TeamsAdapter(_make_config( client_id="bot-id", client_secret="secret", tenant_id="tenant", @@ -669,7 +669,7 @@ class TestTeamsMessageHandling: adapter.handle_message.assert_not_awaited() - @pytest.mark.asyncio + @pytest.mark.anyio async def test_bot_mention_stripped_from_text(self): adapter = TeamsAdapter(_make_config( client_id="bot-id", client_secret="secret", tenant_id="tenant", @@ -687,7 +687,7 @@ class TestTeamsMessageHandling: event = adapter.handle_message.call_args[0][0] assert event.text == "what is the weather?" - @pytest.mark.asyncio + @pytest.mark.anyio async def test_deduplication(self): adapter = TeamsAdapter(_make_config( client_id="bot-id", client_secret="secret", tenant_id="tenant",