mirror of
https://github.com/NousResearch/hermes-agent.git
synced 2026-05-13 03:52:00 +00:00
fix(teams-pipeline): add skill asset and fix async test env
This commit is contained in:
parent
b79ef8827f
commit
729a659a3c
2 changed files with 64 additions and 14 deletions
50
skills/productivity/teams-meeting-pipeline/SKILL.md
Normal file
50
skills/productivity/teams-meeting-pipeline/SKILL.md
Normal file
|
|
@ -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 <job-id>
|
||||||
|
hermes teams-pipeline replay <job-id>
|
||||||
|
hermes teams-pipeline fetch --meeting-id <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.
|
||||||
|
|
@ -360,7 +360,7 @@ class TestTeamsInteractiveSetup:
|
||||||
assert "TEAMS_TENANT_ID=tenant-id" in env_text
|
assert "TEAMS_TENANT_ID=tenant-id" in env_text
|
||||||
|
|
||||||
class TestTeamsConnect:
|
class TestTeamsConnect:
|
||||||
@pytest.mark.asyncio
|
@pytest.mark.anyio
|
||||||
async def test_connect_fails_without_sdk(self, monkeypatch):
|
async def test_connect_fails_without_sdk(self, monkeypatch):
|
||||||
monkeypatch.setattr(_teams_mod, "TEAMS_SDK_AVAILABLE", False)
|
monkeypatch.setattr(_teams_mod, "TEAMS_SDK_AVAILABLE", False)
|
||||||
adapter = TeamsAdapter(_make_config(
|
adapter = TeamsAdapter(_make_config(
|
||||||
|
|
@ -369,7 +369,7 @@ class TestTeamsConnect:
|
||||||
result = await adapter.connect()
|
result = await adapter.connect()
|
||||||
assert result is False
|
assert result is False
|
||||||
|
|
||||||
@pytest.mark.asyncio
|
@pytest.mark.anyio
|
||||||
async def test_connect_fails_without_credentials(self):
|
async def test_connect_fails_without_credentials(self):
|
||||||
adapter = TeamsAdapter(_make_config())
|
adapter = TeamsAdapter(_make_config())
|
||||||
adapter._client_id = ""
|
adapter._client_id = ""
|
||||||
|
|
@ -378,7 +378,7 @@ class TestTeamsConnect:
|
||||||
result = await adapter.connect()
|
result = await adapter.connect()
|
||||||
assert result is False
|
assert result is False
|
||||||
|
|
||||||
@pytest.mark.asyncio
|
@pytest.mark.anyio
|
||||||
async def test_disconnect_cleans_up(self):
|
async def test_disconnect_cleans_up(self):
|
||||||
adapter = TeamsAdapter(_make_config(
|
adapter = TeamsAdapter(_make_config(
|
||||||
client_id="id", client_secret="secret", tenant_id="tenant",
|
client_id="id", client_secret="secret", tenant_id="tenant",
|
||||||
|
|
@ -400,7 +400,7 @@ class TestTeamsConnect:
|
||||||
# ---------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------
|
||||||
|
|
||||||
class TestTeamsSend:
|
class TestTeamsSend:
|
||||||
@pytest.mark.asyncio
|
@pytest.mark.anyio
|
||||||
async def test_send_returns_error_without_app(self):
|
async def test_send_returns_error_without_app(self):
|
||||||
adapter = TeamsAdapter(_make_config(
|
adapter = TeamsAdapter(_make_config(
|
||||||
client_id="id", client_secret="secret", tenant_id="tenant",
|
client_id="id", client_secret="secret", tenant_id="tenant",
|
||||||
|
|
@ -410,7 +410,7 @@ class TestTeamsSend:
|
||||||
assert result.success is False
|
assert result.success is False
|
||||||
assert "not initialized" in result.error
|
assert "not initialized" in result.error
|
||||||
|
|
||||||
@pytest.mark.asyncio
|
@pytest.mark.anyio
|
||||||
async def test_send_calls_app_send(self):
|
async def test_send_calls_app_send(self):
|
||||||
adapter = TeamsAdapter(_make_config(
|
adapter = TeamsAdapter(_make_config(
|
||||||
client_id="id", client_secret="secret", tenant_id="tenant",
|
client_id="id", client_secret="secret", tenant_id="tenant",
|
||||||
|
|
@ -426,7 +426,7 @@ class TestTeamsSend:
|
||||||
assert result.message_id == "msg-123"
|
assert result.message_id == "msg-123"
|
||||||
mock_app.send.assert_awaited_once_with("conv-id", "Hello")
|
mock_app.send.assert_awaited_once_with("conv-id", "Hello")
|
||||||
|
|
||||||
@pytest.mark.asyncio
|
@pytest.mark.anyio
|
||||||
async def test_send_handles_error(self):
|
async def test_send_handles_error(self):
|
||||||
adapter = TeamsAdapter(_make_config(
|
adapter = TeamsAdapter(_make_config(
|
||||||
client_id="id", client_secret="secret", tenant_id="tenant",
|
client_id="id", client_secret="secret", tenant_id="tenant",
|
||||||
|
|
@ -439,7 +439,7 @@ class TestTeamsSend:
|
||||||
assert result.success is False
|
assert result.success is False
|
||||||
assert "Network error" in result.error
|
assert "Network error" in result.error
|
||||||
|
|
||||||
@pytest.mark.asyncio
|
@pytest.mark.anyio
|
||||||
async def test_send_typing(self):
|
async def test_send_typing(self):
|
||||||
adapter = TeamsAdapter(_make_config(
|
adapter = TeamsAdapter(_make_config(
|
||||||
client_id="id", client_secret="secret", tenant_id="tenant",
|
client_id="id", client_secret="secret", tenant_id="tenant",
|
||||||
|
|
@ -594,7 +594,7 @@ class TestTeamsMessageHandling:
|
||||||
ctx.activity = activity
|
ctx.activity = activity
|
||||||
return ctx
|
return ctx
|
||||||
|
|
||||||
@pytest.mark.asyncio
|
@pytest.mark.anyio
|
||||||
async def test_personal_message_creates_dm_event(self):
|
async def test_personal_message_creates_dm_event(self):
|
||||||
adapter = TeamsAdapter(_make_config(
|
adapter = TeamsAdapter(_make_config(
|
||||||
client_id="bot-id", client_secret="secret", tenant_id="tenant",
|
client_id="bot-id", client_secret="secret", tenant_id="tenant",
|
||||||
|
|
@ -610,7 +610,7 @@ class TestTeamsMessageHandling:
|
||||||
event = adapter.handle_message.call_args[0][0]
|
event = adapter.handle_message.call_args[0][0]
|
||||||
assert event.source.chat_type == "dm"
|
assert event.source.chat_type == "dm"
|
||||||
|
|
||||||
@pytest.mark.asyncio
|
@pytest.mark.anyio
|
||||||
async def test_group_message_creates_group_event(self):
|
async def test_group_message_creates_group_event(self):
|
||||||
adapter = TeamsAdapter(_make_config(
|
adapter = TeamsAdapter(_make_config(
|
||||||
client_id="bot-id", client_secret="secret", tenant_id="tenant",
|
client_id="bot-id", client_secret="secret", tenant_id="tenant",
|
||||||
|
|
@ -625,7 +625,7 @@ class TestTeamsMessageHandling:
|
||||||
event = adapter.handle_message.call_args[0][0]
|
event = adapter.handle_message.call_args[0][0]
|
||||||
assert event.source.chat_type == "group"
|
assert event.source.chat_type == "group"
|
||||||
|
|
||||||
@pytest.mark.asyncio
|
@pytest.mark.anyio
|
||||||
async def test_channel_message_creates_channel_event(self):
|
async def test_channel_message_creates_channel_event(self):
|
||||||
adapter = TeamsAdapter(_make_config(
|
adapter = TeamsAdapter(_make_config(
|
||||||
client_id="bot-id", client_secret="secret", tenant_id="tenant",
|
client_id="bot-id", client_secret="secret", tenant_id="tenant",
|
||||||
|
|
@ -640,7 +640,7 @@ class TestTeamsMessageHandling:
|
||||||
event = adapter.handle_message.call_args[0][0]
|
event = adapter.handle_message.call_args[0][0]
|
||||||
assert event.source.chat_type == "channel"
|
assert event.source.chat_type == "channel"
|
||||||
|
|
||||||
@pytest.mark.asyncio
|
@pytest.mark.anyio
|
||||||
async def test_user_id_uses_aad_object_id(self):
|
async def test_user_id_uses_aad_object_id(self):
|
||||||
adapter = TeamsAdapter(_make_config(
|
adapter = TeamsAdapter(_make_config(
|
||||||
client_id="bot-id", client_secret="secret", tenant_id="tenant",
|
client_id="bot-id", client_secret="secret", tenant_id="tenant",
|
||||||
|
|
@ -655,7 +655,7 @@ class TestTeamsMessageHandling:
|
||||||
event = adapter.handle_message.call_args[0][0]
|
event = adapter.handle_message.call_args[0][0]
|
||||||
assert event.source.user_id == "aad-stable-id"
|
assert event.source.user_id == "aad-stable-id"
|
||||||
|
|
||||||
@pytest.mark.asyncio
|
@pytest.mark.anyio
|
||||||
async def test_self_message_filtered(self):
|
async def test_self_message_filtered(self):
|
||||||
adapter = TeamsAdapter(_make_config(
|
adapter = TeamsAdapter(_make_config(
|
||||||
client_id="bot-id", client_secret="secret", tenant_id="tenant",
|
client_id="bot-id", client_secret="secret", tenant_id="tenant",
|
||||||
|
|
@ -669,7 +669,7 @@ class TestTeamsMessageHandling:
|
||||||
|
|
||||||
adapter.handle_message.assert_not_awaited()
|
adapter.handle_message.assert_not_awaited()
|
||||||
|
|
||||||
@pytest.mark.asyncio
|
@pytest.mark.anyio
|
||||||
async def test_bot_mention_stripped_from_text(self):
|
async def test_bot_mention_stripped_from_text(self):
|
||||||
adapter = TeamsAdapter(_make_config(
|
adapter = TeamsAdapter(_make_config(
|
||||||
client_id="bot-id", client_secret="secret", tenant_id="tenant",
|
client_id="bot-id", client_secret="secret", tenant_id="tenant",
|
||||||
|
|
@ -687,7 +687,7 @@ class TestTeamsMessageHandling:
|
||||||
event = adapter.handle_message.call_args[0][0]
|
event = adapter.handle_message.call_args[0][0]
|
||||||
assert event.text == "what is the weather?"
|
assert event.text == "what is the weather?"
|
||||||
|
|
||||||
@pytest.mark.asyncio
|
@pytest.mark.anyio
|
||||||
async def test_deduplication(self):
|
async def test_deduplication(self):
|
||||||
adapter = TeamsAdapter(_make_config(
|
adapter = TeamsAdapter(_make_config(
|
||||||
client_id="bot-id", client_secret="secret", tenant_id="tenant",
|
client_id="bot-id", client_secret="secret", tenant_id="tenant",
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue