Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -296,6 +296,22 @@ BROWSER_INACTIVITY_TIMEOUT=120
# TELEGRAM_WEBHOOK_PORT=8443
# TELEGRAM_WEBHOOK_SECRET= # Recommended for production

# =============================================================================
# MICROSOFT TEAMS INTEGRATION
# =============================================================================
# Register a Bot in Azure: https://dev.botframework.com/ → "Register a bot"
# Or use Azure Portal: Azure Active Directory → App registrations → New registration
# Then add the bot to Teams via the Bot Framework or App Studio.
#
# TEAMS_CLIENT_ID= # Azure AD App (client) ID
# TEAMS_CLIENT_SECRET= # Azure AD client secret value
# TEAMS_TENANT_ID= # Azure AD tenant ID (or "common" for multi-tenant)
# TEAMS_ALLOWED_USERS= # Comma-separated AAD object IDs or UPNs
# TEAMS_ALLOW_ALL_USERS=false # Set true to skip the allowlist
# TEAMS_HOME_CHANNEL= # Default channel/chat ID for cron delivery
# TEAMS_HOME_CHANNEL_NAME= # Display name for the home channel
# TEAMS_PORT=3978 # Webhook listen port (Bot Framework default)

# WhatsApp (built-in Baileys bridge — run `hermes whatsapp` to pair)
# WHATSAPP_ENABLED=false
# WHATSAPP_ALLOWED_USERS=15551234567
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ Use any model you want — [Nous Portal](https://portal.nousresearch.com), [Open

<table>
<tr><td><b>A real terminal interface</b></td><td>Full TUI with multiline editing, slash-command autocomplete, conversation history, interrupt-and-redirect, and streaming tool output.</td></tr>
<tr><td><b>Lives where you do</b></td><td>Telegram, Discord, Slack, WhatsApp, Signal, and CLI — all from a single gateway process. Voice memo transcription, cross-platform conversation continuity.</td></tr>
<tr><td><b>Lives where you do</b></td><td>Telegram, Discord, Slack, WhatsApp, Signal, Microsoft Teams, and CLI — all from a single gateway process. Voice memo transcription, cross-platform conversation continuity.</td></tr>
<tr><td><b>A closed learning loop</b></td><td>Agent-curated memory with periodic nudges. Autonomous skill creation after complex tasks. Skills self-improve during use. FTS5 session search with LLM summarization for cross-session recall. <a href="https://github.com/plastic-labs/honcho">Honcho</a> dialectic user modeling. Compatible with the <a href="https://agentskills.io">agentskills.io</a> open standard.</td></tr>
<tr><td><b>Scheduled automations</b></td><td>Built-in cron scheduler with delivery to any platform. Daily reports, nightly backups, weekly audits — all in natural language, running unattended.</td></tr>
<tr><td><b>Delegates and parallelizes</b></td><td>Spawn isolated subagents for parallel workstreams. Write Python scripts that call tools via RPC, collapsing multi-step pipelines into zero-context-cost turns.</td></tr>
Expand Down
7 changes: 7 additions & 0 deletions agent/prompt_builder.py
Original file line number Diff line number Diff line change
Expand Up @@ -455,6 +455,13 @@ def _strip_yaml_frontmatter(content: str) -> str:
"image and is the WRONG path. Bare Unicode emoji in text is also not a substitute "
"— when a sticker is the right response, use yb_send_sticker."
),
"teams": (
"You are in a Microsoft Teams conversation. Teams supports a subset of markdown: "
"**bold**, *italic*, ~~strikethrough~~, `inline code`, ```code blocks```, "
"[links](url), and numbered/bulleted lists. Tables and headers work in channel "
"posts but may not render in chat messages. Keep messages concise — Teams truncates "
"very long messages."
),
}

# ---------------------------------------------------------------------------
Expand Down
4 changes: 3 additions & 1 deletion cli-config.yaml.example
Original file line number Diff line number Diff line change
Expand Up @@ -570,7 +570,7 @@ agent:
# - A preset like "hermes-cli" or "hermes-telegram" (curated tool set)
# - A list of individual toolsets to compose your own (see list below)
#
# Supported platform keys: cli, telegram, discord, whatsapp, slack, qqbot
# Supported platform keys: cli, telegram, discord, whatsapp, slack, qqbot, teams
#
# Examples:
#
Expand Down Expand Up @@ -600,6 +600,7 @@ agent:
# signal: hermes-signal (same as telegram)
# homeassistant: hermes-homeassistant (same as telegram)
# qqbot: hermes-qqbot (same as telegram)
# teams: hermes-teams (same as telegram)
#
platform_toolsets:
cli: [hermes-cli]
Expand All @@ -611,6 +612,7 @@ platform_toolsets:
homeassistant: [hermes-homeassistant]
qqbot: [hermes-qqbot]
yuanbao: [hermes-yuanbao]
teams: [hermes-teams]

# =============================================================================
# Gateway Platform Settings
Expand Down
1 change: 1 addition & 0 deletions cron/scheduler.py
Original file line number Diff line number Diff line change
Expand Up @@ -360,6 +360,7 @@ def _deliver_result(job: dict, content: str, adapters=None, loop=None) -> Option
"bluebubbles": Platform.BLUEBUBBLES,
"qqbot": Platform.QQBOT,
"yuanbao": Platform.YUANBAO,
"teams": Platform.TEAMS,
}

# Optionally wrap the content with a header/footer so the user knows this
Expand Down
18 changes: 15 additions & 3 deletions docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -24,12 +24,23 @@ services:
image: hermes-agent
container_name: hermes
restart: unless-stopped
network_mode: host
ports:
# Teams webhook — required for Microsoft Teams to deliver messages.
# Change the left side if 3978 is already in use on your host.
# Override TEAMS_PORT in .env if you change it here.
- "3978:3978"
volumes:
- ~/.hermes:/opt/data
environment:
- HERMES_UID=${HERMES_UID:-10000}
- HERMES_GID=${HERMES_GID:-10000}
# Microsoft Teams — uncomment and fill in to enable Teams gateway.
# Register your bot at https://dev.botframework.com/ to get these values.
# - TEAMS_CLIENT_ID=${TEAMS_CLIENT_ID}
# - TEAMS_CLIENT_SECRET=${TEAMS_CLIENT_SECRET}
# - TEAMS_TENANT_ID=${TEAMS_TENANT_ID}
# - TEAMS_ALLOWED_USERS=${TEAMS_ALLOWED_USERS}
# - TEAMS_PORT=3978
# To expose the OpenAI-compatible API server beyond localhost,
# uncomment BOTH lines (API_SERVER_KEY is mandatory for auth):
# - API_SERVER_HOST=0.0.0.0
Expand All @@ -40,7 +51,8 @@ services:
image: hermes-agent
container_name: hermes-dashboard
restart: unless-stopped
network_mode: host
ports:
- "127.0.0.1:9119:9119"
depends_on:
- gateway
volumes:
Expand All @@ -49,4 +61,4 @@ services:
- HERMES_UID=${HERMES_UID:-10000}
- HERMES_GID=${HERMES_GID:-10000}
# Localhost-only. For remote access, tunnel via `ssh -L 9119:localhost:9119`.
command: ["dashboard", "--host", "127.0.0.1", "--no-open"]
command: ["dashboard", "--host", "0.0.0.0", "--no-open"]
39 changes: 38 additions & 1 deletion gateway/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ class Platform(Enum):
BLUEBUBBLES = "bluebubbles"
QQBOT = "qqbot"
YUANBAO = "yuanbao"
TEAMS = "teams"


@dataclass
Expand Down Expand Up @@ -337,7 +338,16 @@ def get_connected_platforms(self) -> List[Platform]:
config.extra.get("client_secret") or os.getenv("DINGTALK_CLIENT_SECRET")
):
connected.append(platform)

# Teams uses client_id + client_secret + tenant_id (all required)
elif platform == Platform.TEAMS and (
config.extra.get("client_id") or os.getenv("TEAMS_CLIENT_ID")
) and (
config.extra.get("client_secret") or os.getenv("TEAMS_CLIENT_SECRET")
) and (
config.extra.get("tenant_id") or os.getenv("TEAMS_TENANT_ID")
):
connected.append(platform)

return connected

def get_home_channel(self, platform: Platform) -> Optional[HomeChannel]:
Expand Down Expand Up @@ -1148,6 +1158,33 @@ def _apply_env_overrides(config: GatewayConfig) -> None:
name=os.getenv("DINGTALK_HOME_CHANNEL_NAME", "Home"),
)

# Microsoft Teams
teams_client_id = os.getenv("TEAMS_CLIENT_ID")
teams_client_secret = os.getenv("TEAMS_CLIENT_SECRET")
teams_tenant_id = os.getenv("TEAMS_TENANT_ID")
if teams_client_id and teams_client_secret and teams_tenant_id:
if Platform.TEAMS not in config.platforms:
config.platforms[Platform.TEAMS] = PlatformConfig()
config.platforms[Platform.TEAMS].enabled = True
config.platforms[Platform.TEAMS].extra.update({
"client_id": teams_client_id,
"client_secret": teams_client_secret,
"tenant_id": teams_tenant_id,
})
teams_port = os.getenv("TEAMS_PORT")
if teams_port:
try:
config.platforms[Platform.TEAMS].extra["port"] = int(teams_port)
except ValueError:
pass
teams_home = os.getenv("TEAMS_HOME_CHANNEL")
if teams_home:
config.platforms[Platform.TEAMS].home_channel = HomeChannel(
platform=Platform.TEAMS,
chat_id=teams_home,
name=os.getenv("TEAMS_HOME_CHANNEL_NAME", "Home"),
)

# Feishu / Lark
feishu_app_id = os.getenv("FEISHU_APP_ID")
feishu_app_secret = os.getenv("FEISHU_APP_SECRET")
Expand Down
Loading