hermes-bsd/website/docs/user-guide/features/plugins.md

353 lines
23 KiB
Markdown
Raw Normal View History

---
docs: restructure site navigation — promote features and platforms to top-level (#4116) Major reorganization of the documentation site for better discoverability and navigation. 94 pages across 8 top-level sections (was 5). Structural changes: - Promote Features from 3-level-deep subcategory to top-level section with new Overview hub page categorizing all 26 feature pages - Promote Messaging Platforms from User Guide subcategory to top-level section, add platform comparison matrix (13 platforms x 7 features) - Create new Integrations section with hub page, grouping MCP, ACP, API Server, Honcho, Provider Routing, Fallback Providers - Extract AI provider content (626 lines) from configuration.md into dedicated integrations/providers.md — configuration.md drops from 1803 to 1178 lines - Subcategorize Developer Guide into Architecture, Extending, Internals - Rename "User Guide" to "Using Hermes" for top-level items Orphan fixes (7 pages now reachable via sidebar): - build-a-hermes-plugin.md added to Guides - sms.md added to Messaging Platforms - context-references.md added to Features > Core - plugins.md added to Features > Core - git-worktrees.md added to Using Hermes - checkpoints-and-rollback.md added to Using Hermes - checkpoints.md (30-line stub) deleted, superseded by checkpoints-and-rollback.md (203 lines) New files: - integrations/index.md — Integrations hub page - integrations/providers.md — AI provider setup (extracted) - user-guide/features/overview.md — Features hub page Broken link fixes: - quickstart.md, faq.md: update context-length-detection anchors - configuration.md: update checkpoints link - overview.md: fix checkpoint link path Docusaurus build verified clean (zero broken links/anchors).
2026-03-30 18:39:51 -07:00
sidebar_position: 11
sidebar_label: "Plugins"
title: "Plugins"
description: "Extend Hermes with custom tools, hooks, and integrations via the plugin system"
---
# Plugins
docs: comprehensive documentation audit — fix 9 HIGH, 20+ MEDIUM gaps (#4087) Reference docs fixes: - cli-commands.md: remove non-existent --provider alibaba, add hermes profile/completion/plugins/mcp to top-level table, add --profile/-p global flag, add --source chat option - slash-commands.md: add /yolo and /commands, fix /q alias conflict (resolves to /queue not /quit), add missing aliases (/bg, /set-home, /reload_mcp, /gateway) - toolsets-reference.md: fix hermes-api-server (not same as hermes-cli, omits clarify/send_message/text_to_speech) - profile-commands.md: fix show name required not optional, --clone-from not --from, add --remove/--name to alias, fix alias path, fix export/ import arg types, remove non-existent fish completion - tools-reference.md: add EXA_API_KEY to web tools requires_env - mcp-config-reference.md: add auth key for OAuth, tool name sanitization - environment-variables.md: add EXA_API_KEY, update provider values - plugins.md: remove non-existent ctx.register_command(), add ctx.inject_message() Feature docs additions: - security.md: add /yolo mode, approval modes (manual/smart/off), configurable timeout, expanded dangerous patterns table - cron.md: add wrap_response config, [SILENT] suppression - mcp.md: add dynamic tool discovery, MCP sampling support - cli.md: add Ctrl+Z suspend, busy_input_mode, tool_preview_length - docker.md: add skills/credential file mounting Messaging platform docs: - telegram.md: add webhook mode, DoH fallback IPs - slack.md: add multi-workspace OAuth support - discord.md: add DISCORD_IGNORE_NO_MENTION - matrix.md: add MSC3245 native voice messages - feishu.md: expand from 129 to 365 lines (encrypt key, verification token, group policy, card actions, media, rate limiting, markdown, troubleshooting) - wecom.md: expand from 86 to 264 lines (per-group allowlists, media, AES decryption, stream replies, reconnection, troubleshooting) Configuration docs: - quickstart.md: add DeepSeek, Copilot, Copilot ACP providers - configuration.md: add DeepSeek provider, Exa web backend, terminal env_passthrough/images, browser.command_timeout, compression params, discord config, security/tirith config, timezone, auxiliary models 21 files changed, ~1000 lines added
2026-03-30 17:15:21 -07:00
Hermes has a plugin system for adding custom tools, hooks, and integrations without modifying core code.
If you want to create a custom tool for yourself, your team, or one project,
this is usually the right path. The developer guide's
[Adding Tools](/docs/developer-guide/adding-tools) page is for built-in Hermes
core tools that live in `tools/` and `toolsets.py`.
**→ [Build a Hermes Plugin](/docs/guides/build-a-hermes-plugin)** — step-by-step guide with a complete working example.
## Quick overview
Drop a directory into `~/.hermes/plugins/` with a `plugin.yaml` and Python code:
```
~/.hermes/plugins/my-plugin/
├── plugin.yaml # manifest
├── __init__.py # register() — wires schemas to handlers
├── schemas.py # tool schemas (what the LLM sees)
└── tools.py # tool handlers (what runs when called)
```
Start Hermes — your tools appear alongside built-in tools. The model can call them immediately.
docs: deep quality pass — expand 10 thin pages, fix specific issues (#4134) Developer guide stubs expanded to full documentation: - trajectory-format.md: 56→233 lines (JSONL format, ShareGPT example, normalization rules, reasoning markup, replay code) - session-storage.md: 66→388 lines (SQLite schema, migration table, FTS5 search syntax, lineage queries, Python API examples) - context-compression-and-caching.md: 72→321 lines (dual compression system, config defaults, 4-phase algorithm, before/after example, prompt caching mechanics, cache-aware patterns) - tools-runtime.md: 65→246 lines (registry API, dispatch flow, availability checking, error wrapping, approval flow) - prompt-assembly.md: 89→246 lines (concrete assembled prompt example, SOUL.md injection, context file discovery table) User-facing pages expanded: - docker.md: 62→224 lines (volumes, env forwarding, docker-compose, resource limits, troubleshooting) - updating.md: 79→167 lines (update behavior, version checking, rollback instructions, Nix users) - skins.md: 80→206 lines (all color/spinner/branding keys, built-in skin descriptions, full custom skin YAML template) Hub pages improved: - integrations/index.md: 25→82 lines (web search backends table, TTS/browser providers, quick config example) - features/overview.md: added Integrations section with 6 missing links Specific fixes: - configuration.md: removed duplicate Gateway Streaming section - mcp.md: removed internal "PR work" language - plugins.md: added inline minimal plugin example (self-contained) 13 files changed, ~1700 lines added. Docusaurus build verified clean.
2026-03-30 20:30:11 -07:00
### Minimal working example
Here is a complete plugin that adds a `hello_world` tool and logs every tool call via a hook.
**`~/.hermes/plugins/hello-world/plugin.yaml`**
```yaml
name: hello-world
version: "1.0"
description: A minimal example plugin
```
**`~/.hermes/plugins/hello-world/__init__.py`**
```python
"""Minimal Hermes plugin — registers a tool and a hook."""
import json
docs: deep quality pass — expand 10 thin pages, fix specific issues (#4134) Developer guide stubs expanded to full documentation: - trajectory-format.md: 56→233 lines (JSONL format, ShareGPT example, normalization rules, reasoning markup, replay code) - session-storage.md: 66→388 lines (SQLite schema, migration table, FTS5 search syntax, lineage queries, Python API examples) - context-compression-and-caching.md: 72→321 lines (dual compression system, config defaults, 4-phase algorithm, before/after example, prompt caching mechanics, cache-aware patterns) - tools-runtime.md: 65→246 lines (registry API, dispatch flow, availability checking, error wrapping, approval flow) - prompt-assembly.md: 89→246 lines (concrete assembled prompt example, SOUL.md injection, context file discovery table) User-facing pages expanded: - docker.md: 62→224 lines (volumes, env forwarding, docker-compose, resource limits, troubleshooting) - updating.md: 79→167 lines (update behavior, version checking, rollback instructions, Nix users) - skins.md: 80→206 lines (all color/spinner/branding keys, built-in skin descriptions, full custom skin YAML template) Hub pages improved: - integrations/index.md: 25→82 lines (web search backends table, TTS/browser providers, quick config example) - features/overview.md: added Integrations section with 6 missing links Specific fixes: - configuration.md: removed duplicate Gateway Streaming section - mcp.md: removed internal "PR work" language - plugins.md: added inline minimal plugin example (self-contained) 13 files changed, ~1700 lines added. Docusaurus build verified clean.
2026-03-30 20:30:11 -07:00
def register(ctx):
# --- Tool: hello_world ---
schema = {
"name": "hello_world",
"description": "Returns a friendly greeting for the given name.",
"parameters": {
"type": "object",
"properties": {
"name": {
"type": "string",
"description": "Name to greet",
}
},
"required": ["name"],
},
}
def handle_hello(params, **kwargs):
del kwargs
docs: deep quality pass — expand 10 thin pages, fix specific issues (#4134) Developer guide stubs expanded to full documentation: - trajectory-format.md: 56→233 lines (JSONL format, ShareGPT example, normalization rules, reasoning markup, replay code) - session-storage.md: 66→388 lines (SQLite schema, migration table, FTS5 search syntax, lineage queries, Python API examples) - context-compression-and-caching.md: 72→321 lines (dual compression system, config defaults, 4-phase algorithm, before/after example, prompt caching mechanics, cache-aware patterns) - tools-runtime.md: 65→246 lines (registry API, dispatch flow, availability checking, error wrapping, approval flow) - prompt-assembly.md: 89→246 lines (concrete assembled prompt example, SOUL.md injection, context file discovery table) User-facing pages expanded: - docker.md: 62→224 lines (volumes, env forwarding, docker-compose, resource limits, troubleshooting) - updating.md: 79→167 lines (update behavior, version checking, rollback instructions, Nix users) - skins.md: 80→206 lines (all color/spinner/branding keys, built-in skin descriptions, full custom skin YAML template) Hub pages improved: - integrations/index.md: 25→82 lines (web search backends table, TTS/browser providers, quick config example) - features/overview.md: added Integrations section with 6 missing links Specific fixes: - configuration.md: removed duplicate Gateway Streaming section - mcp.md: removed internal "PR work" language - plugins.md: added inline minimal plugin example (self-contained) 13 files changed, ~1700 lines added. Docusaurus build verified clean.
2026-03-30 20:30:11 -07:00
name = params.get("name", "World")
return json.dumps({"success": True, "greeting": f"Hello, {name}!"})
docs: deep quality pass — expand 10 thin pages, fix specific issues (#4134) Developer guide stubs expanded to full documentation: - trajectory-format.md: 56→233 lines (JSONL format, ShareGPT example, normalization rules, reasoning markup, replay code) - session-storage.md: 66→388 lines (SQLite schema, migration table, FTS5 search syntax, lineage queries, Python API examples) - context-compression-and-caching.md: 72→321 lines (dual compression system, config defaults, 4-phase algorithm, before/after example, prompt caching mechanics, cache-aware patterns) - tools-runtime.md: 65→246 lines (registry API, dispatch flow, availability checking, error wrapping, approval flow) - prompt-assembly.md: 89→246 lines (concrete assembled prompt example, SOUL.md injection, context file discovery table) User-facing pages expanded: - docker.md: 62→224 lines (volumes, env forwarding, docker-compose, resource limits, troubleshooting) - updating.md: 79→167 lines (update behavior, version checking, rollback instructions, Nix users) - skins.md: 80→206 lines (all color/spinner/branding keys, built-in skin descriptions, full custom skin YAML template) Hub pages improved: - integrations/index.md: 25→82 lines (web search backends table, TTS/browser providers, quick config example) - features/overview.md: added Integrations section with 6 missing links Specific fixes: - configuration.md: removed duplicate Gateway Streaming section - mcp.md: removed internal "PR work" language - plugins.md: added inline minimal plugin example (self-contained) 13 files changed, ~1700 lines added. Docusaurus build verified clean.
2026-03-30 20:30:11 -07:00
ctx.register_tool(
name="hello_world",
toolset="hello_world",
schema=schema,
handler=handle_hello,
description="Return a friendly greeting for the given name.",
)
docs: deep quality pass — expand 10 thin pages, fix specific issues (#4134) Developer guide stubs expanded to full documentation: - trajectory-format.md: 56→233 lines (JSONL format, ShareGPT example, normalization rules, reasoning markup, replay code) - session-storage.md: 66→388 lines (SQLite schema, migration table, FTS5 search syntax, lineage queries, Python API examples) - context-compression-and-caching.md: 72→321 lines (dual compression system, config defaults, 4-phase algorithm, before/after example, prompt caching mechanics, cache-aware patterns) - tools-runtime.md: 65→246 lines (registry API, dispatch flow, availability checking, error wrapping, approval flow) - prompt-assembly.md: 89→246 lines (concrete assembled prompt example, SOUL.md injection, context file discovery table) User-facing pages expanded: - docker.md: 62→224 lines (volumes, env forwarding, docker-compose, resource limits, troubleshooting) - updating.md: 79→167 lines (update behavior, version checking, rollback instructions, Nix users) - skins.md: 80→206 lines (all color/spinner/branding keys, built-in skin descriptions, full custom skin YAML template) Hub pages improved: - integrations/index.md: 25→82 lines (web search backends table, TTS/browser providers, quick config example) - features/overview.md: added Integrations section with 6 missing links Specific fixes: - configuration.md: removed duplicate Gateway Streaming section - mcp.md: removed internal "PR work" language - plugins.md: added inline minimal plugin example (self-contained) 13 files changed, ~1700 lines added. Docusaurus build verified clean.
2026-03-30 20:30:11 -07:00
# --- Hook: log every tool call ---
def on_tool_call(tool_name, params, result):
print(f"[hello-world] tool called: {tool_name}")
ctx.register_hook("post_tool_call", on_tool_call)
```
Drop both files into `~/.hermes/plugins/hello-world/`, restart Hermes, and the model can immediately call `hello_world`. The hook prints a log line after every tool invocation.
Project-local plugins under `./.hermes/plugins/` are disabled by default. Enable them only for trusted repositories by setting `HERMES_ENABLE_PROJECT_PLUGINS=true` before starting Hermes.
## What plugins can do
docs: pluggable surfaces coverage — model-provider guide, full plugin map, opt-in fix (#20749) * docs(providers): add model-provider-plugin authoring guide + fix stale refs New docs: - website/docs/developer-guide/model-provider-plugin.md — full authoring guide (directory layout, minimal example, ProviderProfile fields, overridable hooks, user overrides, api_mode selection, auth types, testing, pip distribution) - Wired into website/sidebars.ts under 'Extending' - Cross-references added in: - guides/build-a-hermes-plugin.md (tip block) - developer-guide/adding-providers.md - developer-guide/provider-runtime.md User guide: - user-guide/features/plugins.md: Plugin types table grows from 3 to 4 with 'Model providers' row Stale comment cleanup (providers/*.py → plugins/model-providers/<name>/): - hermes_cli/main.py:_is_profile_api_key_provider docstring - hermes_cli/doctor.py:_build_apikey_providers_list docstring - hermes_cli/auth.py: PROVIDER_REGISTRY + alias auto-extension comments - hermes_cli/models.py: CANONICAL_PROVIDERS auto-extension comment AGENTS.md: - Project-structure tree: added plugins/model-providers/ row - New section: 'Model-provider plugins' explaining discovery, override semantics, PluginManager integration, kind auto-coerce heuristic Verified: docusaurus build succeeds, new page renders, all 3 cross-links resolve. 347/347 targeted tests pass (tests/providers/, tests/hermes_cli/test_plugins.py, tests/hermes_cli/test_runtime_provider_resolution.py, tests/run_agent/test_provider_parity.py). * docs(plugins): add 'pluggable interfaces at a glance' maps to plugins.md + build-a-hermes-plugin Devs landing on either the user-guide plugin page or the build-a-plugin guide now get an upfront table of every distinct pluggable surface with a link to the right authoring doc. Previously they'd have to read the full general-plugin guide to discover that model providers / platforms / memory / context engines are separate systems. user-guide/features/plugins.md: - New 'Pluggable interfaces — where to go for each' section below the existing 4-kinds table - 10 rows covering every register_* surface (tool, hook, slash command, CLI subcommand, skill, model provider, platform, memory, context engine, image-gen) - Explicit note: TTS/STT are NOT plugin-extensible yet — documented with a pointer to the current config.yaml 'command providers' pattern and a note that register_tts_provider()/register_stt_provider() may come later guides/build-a-hermes-plugin.md: - New :::info 'Not sure which guide you need?' map at the top so devs see all pluggable interfaces before investing in this 737-line general-plugin walkthrough - Existing bottom :::tip expanded to include platform adapters alongside model/memory/context plugins Verified: - All 8 cross-doc links in the new plugins.md table resolve in a docusaurus build (SUCCESS, no new broken links) - TTS link corrected (features/voice → features/tts; latter exists) - Pre-existing broken links/anchors (cron-script-only, llms.txt, adding-platform-adapters#step-by-step-checklist) are unchanged * docs(plugins): correct TTS/STT pluggability \u2014 they ARE plugins (command-providers) Previous commit incorrectly said TTS/STT 'aren't plugin-extensible'. They are, via the config-driven command-provider pattern \u2014 any CLI that reads text and writes audio (or vice versa for STT) is automatically a plugin with zero Python. The tts.md docs cover this extensively and I missed it. plugins.md: - TTS row: 'Config-driven (not a Python plugin)', points at tts.md#custom-command-providers - STT row: points at tts.md#voice-message-transcription-stt (STT docs live in tts.md despite the filename) - Expanded note: TTS/STT use config-driven shell-command templates as their plugin surface (full tts.providers.<name> registry for TTS; HERMES_LOCAL_STT_COMMAND escape hatch for STT) - Any CLI that reads/writes files is automatically a plugin \u2014 no Python register_* API needed - Future register_tts_provider()/register_stt_provider() hooks mentioned as nice-to-have for SDK/streaming cases, not as the primary story build-a-hermes-plugin.md: - Same map update: TTS/STT rows explicit, footer note corrected Verified: - tts.md anchors (custom-command-providers, voice-message-transcription-stt) exist and resolve in docusaurus build (SUCCESS, no new broken links) * docs(plugins): expand pluggable interfaces table with MCP / event hooks / shell hooks / skill taps Broadened the scope beyond Python register_* hooks. Hermes has MULTIPLE plugin-style extension surfaces; they're now all in one table instead of being scattered across feature docs. Added rows for: - **MCP servers** — config.yaml mcp_servers.<name> auto-registers external tools from any MCP server. Huge extensibility surface, previously not linked from the plugin map. - **Gateway event hooks** — drop HOOK.yaml + handler.py into ~/.hermes/hooks/<name>/ to fire on gateway:startup, session:*, agent:*, command:* events. Separate from Python plugin hooks. - **Shell hooks** — hooks: block in config.yaml runs shell commands on events (notifications, auditing, etc.). - **Skill sources (taps)** — hermes skills tap add <repo> to pull in new skill registries beyond the built-in sources. Both docs updated: - user-guide/features/plugins.md: table column renamed to 'How' (mixes Python API + config-driven + drop-in-dir surfaces accurately) - guides/build-a-hermes-plugin.md: :::info map at top mirrors the new surfaces with a forward-link to the consolidated table Note block rewritten: instead of singling out TTS/STT as the 'different style' exception, now honestly describes that Hermes deliberately supports three plugin styles — Python APIs, config-driven commands, and drop-in manifest directories — and devs should pick the one that fits their integration. Not included (considered and rejected): - Transport layer (register_transport) — internal, not user-facing - Tool-call parsers — internal, VLLM phase-2 thing - Cloud browser providers — hardcoded registry, not drop-in yet - Terminal backends — hardcoded if/elif, not drop-in yet - Skill sources (the ABC) — hardcoded list, only taps are user-extensible Verified: - All 5 new anchors resolve (gateway-event-hooks, shell-hooks, skills-hub, custom-command-providers, voice-message-transcription-stt) - Docusaurus build SUCCESS, zero new broken links - Same 3 pre-existing broken links on main (cron-script-only, llms.txt, adding-platform-adapters#step-by-step-checklist) * docs(plugins): cover every pluggable surface in both the overview and how-to Both plugins.md and build-a-hermes-plugin.md now cover every extension surface end-to-end \u2014 general plugin APIs, specialized plugin types, config-driven surfaces \u2014 with concrete authoring patterns for each. plugins.md: - 'What plugins can do' table grows from 9 rows (general ctx.register_* only) to 14 rows covering register_platform, register_image_gen_provider, register_context_engine, MemoryProvider subclass, register_provider (model). Each row links to its full authoring guide. - New 'Plugin sub-categories' section under Plugin Discovery explains how plugins/platforms/, plugins/image_gen/, plugins/memory/, plugins/context_engine/, plugins/model-providers/ are routed to different loaders \u2014 PluginManager vs the per-category own-loader systems. - Explicit mention of user-override semantics at ~/.hermes/plugins/model-providers/ and ~/.hermes/plugins/memory/. build-a-hermes-plugin.md: - New '## Specialized plugin types' section (5 sub-sections): - Model provider plugins \u2014 ProviderProfile + plugin.yaml example, auto-wiring summary, link to full guide - Platform plugins \u2014 BasePlatformAdapter + register_platform() skeleton - Memory provider plugins \u2014 MemoryProvider subclass example - Context engine plugins \u2014 ContextEngine subclass example - Image-generation backends \u2014 ImageGenProvider + kind: backend example - New '## Non-Python extension surfaces' section (5 sub-sections): - MCP servers \u2014 config.yaml mcp_servers.<name> example - Gateway event hooks \u2014 HOOK.yaml + handler.py example - Shell hooks \u2014 hooks: block in config.yaml example - Skill sources (taps) \u2014 hermes skills tap add example - TTS / STT command templates \u2014 tts.providers.<name> with type: command - Distribute via pip / NixOS promoted from ### to ## (they were orphaned after the reorganization) Each specialized / non-Python section has a concrete, copy-pasteable example plus a 'Full guide:' link to the authoritative doc. Devs arriving at the build-a-hermes-plugin guide now see every extension surface at their disposal, not just the general tool/hook/slash-command surface. Verified: - Docusaurus build SUCCESS, zero new broken links - All new cross-links (developer-guide/model-provider-plugin, adding-platform-adapters, memory-provider-plugin, context-engine-plugin, user-guide/features/mcp, skills#skills-hub, hooks#gateway-event-hooks, hooks#shell-hooks, tts#custom-command-providers, tts#voice-message-transcription-stt) resolve - Same 3 pre-existing broken links on main (cron-script-only, llms.txt, adding-platform-adapters#step-by-step-checklist) * docs(plugins): fix opt-in inconsistency — not every plugin is gated The 'Every plugin is disabled by default' statement was wrong. Several plugin categories intentionally bypass plugins.enabled: - Bundled platform plugins (IRC, Teams) auto-load so shipped gateway channels are available out of the box. Activation per channel is via gateway.platforms.<name>.enabled. - Bundled backends (plugins/image_gen/*) auto-load so the default backend 'just works'. Selection via <category>.provider config. - Memory providers are all discovered; one is active via memory.provider. - Context engines are all discovered; one is active via context.engine. - Model providers: all 33 discovered at first get_provider_profile(); user picks via --provider / config. The plugins.enabled allow-list specifically gates: - Standalone plugins (general tools/hooks/slash commands) - User-installed backends - User-installed platforms (third-party gateway adapters) - Pip entry-point backends Which matches the actual code in hermes_cli/plugins.py:737 where the bundled+backend/platform check bypasses the allow-list. Rewrote '## Plugins are opt-in' to: - Retitle to 'Plugins are opt-in (with a few exceptions)' - Narrow opening claim to 'General plugins and user-installed backends are disabled by default' - Added 'What the allow-list does NOT gate' subsection with a full table of which bypass the gate and how they're activated instead - Fixed migration section wording (bundled platform/backend plugins never needed grandfathering) Verified: docusaurus build SUCCESS, zero new broken links.
2026-05-06 07:24:42 -07:00
Every `ctx.*` API below is available inside a plugin's `register(ctx)` function.
| Capability | How |
|-----------|-----|
| Add tools | `ctx.register_tool(name=..., toolset=..., schema=..., handler=...)` |
| Add hooks | `ctx.register_hook("post_tool_call", callback)` |
| Add slash commands | `ctx.register_command(name, handler, description)` — adds `/name` in CLI and gateway sessions |
| Dispatch tools from commands | `ctx.dispatch_tool(name, args)` — invokes a registered tool with parent-agent context auto-wired |
| Add CLI commands | `ctx.register_cli_command(name, help, setup_fn, handler_fn)` — adds `hermes <plugin> <subcommand>` |
docs: comprehensive documentation audit — fix 9 HIGH, 20+ MEDIUM gaps (#4087) Reference docs fixes: - cli-commands.md: remove non-existent --provider alibaba, add hermes profile/completion/plugins/mcp to top-level table, add --profile/-p global flag, add --source chat option - slash-commands.md: add /yolo and /commands, fix /q alias conflict (resolves to /queue not /quit), add missing aliases (/bg, /set-home, /reload_mcp, /gateway) - toolsets-reference.md: fix hermes-api-server (not same as hermes-cli, omits clarify/send_message/text_to_speech) - profile-commands.md: fix show name required not optional, --clone-from not --from, add --remove/--name to alias, fix alias path, fix export/ import arg types, remove non-existent fish completion - tools-reference.md: add EXA_API_KEY to web tools requires_env - mcp-config-reference.md: add auth key for OAuth, tool name sanitization - environment-variables.md: add EXA_API_KEY, update provider values - plugins.md: remove non-existent ctx.register_command(), add ctx.inject_message() Feature docs additions: - security.md: add /yolo mode, approval modes (manual/smart/off), configurable timeout, expanded dangerous patterns table - cron.md: add wrap_response config, [SILENT] suppression - mcp.md: add dynamic tool discovery, MCP sampling support - cli.md: add Ctrl+Z suspend, busy_input_mode, tool_preview_length - docker.md: add skills/credential file mounting Messaging platform docs: - telegram.md: add webhook mode, DoH fallback IPs - slack.md: add multi-workspace OAuth support - discord.md: add DISCORD_IGNORE_NO_MENTION - matrix.md: add MSC3245 native voice messages - feishu.md: expand from 129 to 365 lines (encrypt key, verification token, group policy, card actions, media, rate limiting, markdown, troubleshooting) - wecom.md: expand from 86 to 264 lines (per-group allowlists, media, AES decryption, stream replies, reconnection, troubleshooting) Configuration docs: - quickstart.md: add DeepSeek, Copilot, Copilot ACP providers - configuration.md: add DeepSeek provider, Exa web backend, terminal env_passthrough/images, browser.command_timeout, compression params, discord config, security/tirith config, timezone, auxiliary models 21 files changed, ~1000 lines added
2026-03-30 17:15:21 -07:00
| Inject messages | `ctx.inject_message(content, role="user")` — see [Injecting Messages](#injecting-messages) |
| Ship data files | `Path(__file__).parent / "data" / "file.yaml"` |
| Bundle skills | `ctx.register_skill(name, path)` — namespaced as `plugin:skill`, loaded via `skill_view("plugin:skill")` |
| Gate on env vars | `requires_env: [API_KEY]` in plugin.yaml — prompted during `hermes plugins install` |
| Distribute via pip | `[project.entry-points."hermes_agent.plugins"]` |
docs: pluggable surfaces coverage — model-provider guide, full plugin map, opt-in fix (#20749) * docs(providers): add model-provider-plugin authoring guide + fix stale refs New docs: - website/docs/developer-guide/model-provider-plugin.md — full authoring guide (directory layout, minimal example, ProviderProfile fields, overridable hooks, user overrides, api_mode selection, auth types, testing, pip distribution) - Wired into website/sidebars.ts under 'Extending' - Cross-references added in: - guides/build-a-hermes-plugin.md (tip block) - developer-guide/adding-providers.md - developer-guide/provider-runtime.md User guide: - user-guide/features/plugins.md: Plugin types table grows from 3 to 4 with 'Model providers' row Stale comment cleanup (providers/*.py → plugins/model-providers/<name>/): - hermes_cli/main.py:_is_profile_api_key_provider docstring - hermes_cli/doctor.py:_build_apikey_providers_list docstring - hermes_cli/auth.py: PROVIDER_REGISTRY + alias auto-extension comments - hermes_cli/models.py: CANONICAL_PROVIDERS auto-extension comment AGENTS.md: - Project-structure tree: added plugins/model-providers/ row - New section: 'Model-provider plugins' explaining discovery, override semantics, PluginManager integration, kind auto-coerce heuristic Verified: docusaurus build succeeds, new page renders, all 3 cross-links resolve. 347/347 targeted tests pass (tests/providers/, tests/hermes_cli/test_plugins.py, tests/hermes_cli/test_runtime_provider_resolution.py, tests/run_agent/test_provider_parity.py). * docs(plugins): add 'pluggable interfaces at a glance' maps to plugins.md + build-a-hermes-plugin Devs landing on either the user-guide plugin page or the build-a-plugin guide now get an upfront table of every distinct pluggable surface with a link to the right authoring doc. Previously they'd have to read the full general-plugin guide to discover that model providers / platforms / memory / context engines are separate systems. user-guide/features/plugins.md: - New 'Pluggable interfaces — where to go for each' section below the existing 4-kinds table - 10 rows covering every register_* surface (tool, hook, slash command, CLI subcommand, skill, model provider, platform, memory, context engine, image-gen) - Explicit note: TTS/STT are NOT plugin-extensible yet — documented with a pointer to the current config.yaml 'command providers' pattern and a note that register_tts_provider()/register_stt_provider() may come later guides/build-a-hermes-plugin.md: - New :::info 'Not sure which guide you need?' map at the top so devs see all pluggable interfaces before investing in this 737-line general-plugin walkthrough - Existing bottom :::tip expanded to include platform adapters alongside model/memory/context plugins Verified: - All 8 cross-doc links in the new plugins.md table resolve in a docusaurus build (SUCCESS, no new broken links) - TTS link corrected (features/voice → features/tts; latter exists) - Pre-existing broken links/anchors (cron-script-only, llms.txt, adding-platform-adapters#step-by-step-checklist) are unchanged * docs(plugins): correct TTS/STT pluggability \u2014 they ARE plugins (command-providers) Previous commit incorrectly said TTS/STT 'aren't plugin-extensible'. They are, via the config-driven command-provider pattern \u2014 any CLI that reads text and writes audio (or vice versa for STT) is automatically a plugin with zero Python. The tts.md docs cover this extensively and I missed it. plugins.md: - TTS row: 'Config-driven (not a Python plugin)', points at tts.md#custom-command-providers - STT row: points at tts.md#voice-message-transcription-stt (STT docs live in tts.md despite the filename) - Expanded note: TTS/STT use config-driven shell-command templates as their plugin surface (full tts.providers.<name> registry for TTS; HERMES_LOCAL_STT_COMMAND escape hatch for STT) - Any CLI that reads/writes files is automatically a plugin \u2014 no Python register_* API needed - Future register_tts_provider()/register_stt_provider() hooks mentioned as nice-to-have for SDK/streaming cases, not as the primary story build-a-hermes-plugin.md: - Same map update: TTS/STT rows explicit, footer note corrected Verified: - tts.md anchors (custom-command-providers, voice-message-transcription-stt) exist and resolve in docusaurus build (SUCCESS, no new broken links) * docs(plugins): expand pluggable interfaces table with MCP / event hooks / shell hooks / skill taps Broadened the scope beyond Python register_* hooks. Hermes has MULTIPLE plugin-style extension surfaces; they're now all in one table instead of being scattered across feature docs. Added rows for: - **MCP servers** — config.yaml mcp_servers.<name> auto-registers external tools from any MCP server. Huge extensibility surface, previously not linked from the plugin map. - **Gateway event hooks** — drop HOOK.yaml + handler.py into ~/.hermes/hooks/<name>/ to fire on gateway:startup, session:*, agent:*, command:* events. Separate from Python plugin hooks. - **Shell hooks** — hooks: block in config.yaml runs shell commands on events (notifications, auditing, etc.). - **Skill sources (taps)** — hermes skills tap add <repo> to pull in new skill registries beyond the built-in sources. Both docs updated: - user-guide/features/plugins.md: table column renamed to 'How' (mixes Python API + config-driven + drop-in-dir surfaces accurately) - guides/build-a-hermes-plugin.md: :::info map at top mirrors the new surfaces with a forward-link to the consolidated table Note block rewritten: instead of singling out TTS/STT as the 'different style' exception, now honestly describes that Hermes deliberately supports three plugin styles — Python APIs, config-driven commands, and drop-in manifest directories — and devs should pick the one that fits their integration. Not included (considered and rejected): - Transport layer (register_transport) — internal, not user-facing - Tool-call parsers — internal, VLLM phase-2 thing - Cloud browser providers — hardcoded registry, not drop-in yet - Terminal backends — hardcoded if/elif, not drop-in yet - Skill sources (the ABC) — hardcoded list, only taps are user-extensible Verified: - All 5 new anchors resolve (gateway-event-hooks, shell-hooks, skills-hub, custom-command-providers, voice-message-transcription-stt) - Docusaurus build SUCCESS, zero new broken links - Same 3 pre-existing broken links on main (cron-script-only, llms.txt, adding-platform-adapters#step-by-step-checklist) * docs(plugins): cover every pluggable surface in both the overview and how-to Both plugins.md and build-a-hermes-plugin.md now cover every extension surface end-to-end \u2014 general plugin APIs, specialized plugin types, config-driven surfaces \u2014 with concrete authoring patterns for each. plugins.md: - 'What plugins can do' table grows from 9 rows (general ctx.register_* only) to 14 rows covering register_platform, register_image_gen_provider, register_context_engine, MemoryProvider subclass, register_provider (model). Each row links to its full authoring guide. - New 'Plugin sub-categories' section under Plugin Discovery explains how plugins/platforms/, plugins/image_gen/, plugins/memory/, plugins/context_engine/, plugins/model-providers/ are routed to different loaders \u2014 PluginManager vs the per-category own-loader systems. - Explicit mention of user-override semantics at ~/.hermes/plugins/model-providers/ and ~/.hermes/plugins/memory/. build-a-hermes-plugin.md: - New '## Specialized plugin types' section (5 sub-sections): - Model provider plugins \u2014 ProviderProfile + plugin.yaml example, auto-wiring summary, link to full guide - Platform plugins \u2014 BasePlatformAdapter + register_platform() skeleton - Memory provider plugins \u2014 MemoryProvider subclass example - Context engine plugins \u2014 ContextEngine subclass example - Image-generation backends \u2014 ImageGenProvider + kind: backend example - New '## Non-Python extension surfaces' section (5 sub-sections): - MCP servers \u2014 config.yaml mcp_servers.<name> example - Gateway event hooks \u2014 HOOK.yaml + handler.py example - Shell hooks \u2014 hooks: block in config.yaml example - Skill sources (taps) \u2014 hermes skills tap add example - TTS / STT command templates \u2014 tts.providers.<name> with type: command - Distribute via pip / NixOS promoted from ### to ## (they were orphaned after the reorganization) Each specialized / non-Python section has a concrete, copy-pasteable example plus a 'Full guide:' link to the authoritative doc. Devs arriving at the build-a-hermes-plugin guide now see every extension surface at their disposal, not just the general tool/hook/slash-command surface. Verified: - Docusaurus build SUCCESS, zero new broken links - All new cross-links (developer-guide/model-provider-plugin, adding-platform-adapters, memory-provider-plugin, context-engine-plugin, user-guide/features/mcp, skills#skills-hub, hooks#gateway-event-hooks, hooks#shell-hooks, tts#custom-command-providers, tts#voice-message-transcription-stt) resolve - Same 3 pre-existing broken links on main (cron-script-only, llms.txt, adding-platform-adapters#step-by-step-checklist) * docs(plugins): fix opt-in inconsistency — not every plugin is gated The 'Every plugin is disabled by default' statement was wrong. Several plugin categories intentionally bypass plugins.enabled: - Bundled platform plugins (IRC, Teams) auto-load so shipped gateway channels are available out of the box. Activation per channel is via gateway.platforms.<name>.enabled. - Bundled backends (plugins/image_gen/*) auto-load so the default backend 'just works'. Selection via <category>.provider config. - Memory providers are all discovered; one is active via memory.provider. - Context engines are all discovered; one is active via context.engine. - Model providers: all 33 discovered at first get_provider_profile(); user picks via --provider / config. The plugins.enabled allow-list specifically gates: - Standalone plugins (general tools/hooks/slash commands) - User-installed backends - User-installed platforms (third-party gateway adapters) - Pip entry-point backends Which matches the actual code in hermes_cli/plugins.py:737 where the bundled+backend/platform check bypasses the allow-list. Rewrote '## Plugins are opt-in' to: - Retitle to 'Plugins are opt-in (with a few exceptions)' - Narrow opening claim to 'General plugins and user-installed backends are disabled by default' - Added 'What the allow-list does NOT gate' subsection with a full table of which bypass the gate and how they're activated instead - Fixed migration section wording (bundled platform/backend plugins never needed grandfathering) Verified: docusaurus build SUCCESS, zero new broken links.
2026-05-06 07:24:42 -07:00
| Register a gateway platform (Discord, Telegram, IRC, …) | `ctx.register_platform(name, label, adapter_factory, check_fn, ...)` — see [Adding Platform Adapters](/docs/developer-guide/adding-platform-adapters) |
docs(plugins): close the gaps \u2014 image-gen-provider-plugin guide + publishing a skill tap (#20800) Two pluggable surfaces were mentioned in the interfaces map without a real authoring guide behind them: 1. **Image-gen backends** — only had 'See bundled examples' pointers. Now a full developer-guide/image-gen-provider-plugin.md (270 lines) mirroring the memory/context/model provider docs: - How discovery works, directory structure, plugin.yaml - ImageGenProvider ABC with every overridable method (name, display_name, is_available, list_models, default_model, get_setup_schema, generate) - Full authoring walkthrough with a working MyBackendImageGenProvider - Response-format reference (success_response / error_response) - Handling b64 vs URL output (save_b64_image helper) - User overrides at ~/.hermes/plugins/image_gen/<name>/ - Testing recipe + pip distribution - Reference examples (openai, openai-codex, xai) 2. **Skill taps** — features/skills.md mentioned the CLI commands but never explained the repo contract for publishing a tap. Added 'Publishing a custom skill tap' section under Skills Hub covering: - Repo layout (skills/<name>/SKILL.md by default) - Minimal working example - Non-default path configuration (taps.json) - Installing individual skills without subscribing - Trust-level handling - Full tap management CLI + in-session /skills tap commands Wired into: - website/sidebars.ts: image-gen-provider-plugin added to Extending group - website/docs/user-guide/features/plugins.md: pluggable interfaces table + 'What plugins can do' table now link to the real guides instead of 'See bundled examples' - website/docs/guides/build-a-hermes-plugin.md: top info map and inline sub-sections updated, 'Full guide:' line added to image-gen block, tap section mentions publishing Verified: docusaurus build SUCCESS, new page renders at /docs/developer-guide/image-gen-provider-plugin, anchor #publishing-a-custom-skill-tap resolves from plugins.md + build-a-hermes-plugin.md. Pre-existing zh-Hans broken links unchanged.
2026-05-06 08:40:05 -07:00
| Register an image-generation backend | `ctx.register_image_gen_provider(provider)` — see [Image Generation Provider Plugins](/docs/developer-guide/image-gen-provider-plugin) |
feat(video_gen): unified video_generate tool with pluggable provider backends (#25126) * feat(video_gen): unified video_generate tool with pluggable provider backends One core video_generate tool, every backend a plugin. Mirrors the image_gen + memory_provider + context_engine architecture: ABC, registry, plugin-context registration hook, and per-plugin model catalogs surfaced through hermes tools. Surface (one schema, every backend): - operation: generate / edit / extend - modalities: text-to-video (prompt only), image-to-video (prompt + image_url), video edit (prompt + video_url), video extend (video_url) - reference_image_urls, duration, aspect_ratio, resolution, negative_prompt, audio, seed, model override - Providers ignore unknown kwargs and declare what they support via VideoGenProvider.capabilities() — backend-specific quirks stay in the backend, the agent learns one tool Backends shipped: - plugins/video_gen/xai/ — Grok-Imagine, full generate/edit/extend + image-to-video + reference images (salvaged from PR #10600 by @Jaaneek, reshaped into the plugin interface) - plugins/video_gen/fal/ — Veo 3.1 (t2v + i2v), Kling O3 i2v, Pixverse v6 i2v with model-aware payload building that drops keys a model doesn't declare Wiring: - agent/video_gen_provider.py — VideoGenProvider ABC, normalize_operation, success_response / error_response, save_b64_video / save_bytes_video, $HERMES_HOME/cache/videos/ - agent/video_gen_registry.py — thread-safe register/get/list + get_active_provider() reading video_gen.provider from config.yaml - hermes_cli/plugins.py — PluginContext.register_video_gen_provider() - hermes_cli/tools_config.py — Video Generation category in hermes tools, plugin-only providers list, model picker per plugin, config write to video_gen.{provider,model} - toolsets.py — new video_gen toolset - tests: 31 new tests covering ABC, registry, tool dispatch, both plugins - docs: developer-guide/video-gen-provider-plugin.md (parallel to the image-gen guide), sidebar + toolsets-reference + plugin guides updated Supersedes: #25035 (FAL), #17972 (FAL), #14543 (xAI), #13847 (HappyHorse), #10458 (provider categories), #10786 (xAI media+search bundle), #2984 (FAL duplicate), #19086 (Google Veo standalone — easy port to plugin interface). Co-authored-by: Jaaneek <Jaaneek@users.noreply.github.com> * feat(video_gen): dynamic schema reflects active backend's capabilities Address the 'capability variance' question — instead of one tool with a static schema that lies about what every backend supports, the video_generate tool now rebuilds its description at get_definitions() time based on the configured video_gen.provider and video_gen.model. The agent sees backend-specific guidance up-front: - 'fal-ai/veo3.1/image-to-video': 'image-to-video only — image_url is REQUIRED; text-only prompts will be rejected' - 'fal-ai/veo3.1' (t2v): no image_url restriction shown - xAI grok-imagine-video: 'operations: generate, edit, extend; up to 7 reference_image_urls' - Backends without edit/extend: 'not supported on this backend — surface that they need to switch backends via hermes tools' This is the same pattern PR #22694 used for delegate_task self-capping — documented in the dynamic-tool-schemas skill. Cache invalidation is free: get_tool_definitions() already memoizes on config.yaml mtime, so a mid-session backend swap rebuilds the schema automatically. Tested: - Empirical FAL OpenAPI schema check confirms image-to-video models require image_url (FAL returns HTTP 422 otherwise) — client-side rejection in FALVideoGenProvider.generate() now prevents the wasted round-trip - Live E2E: fal-ai/veo3.1/image-to-video + prompt-only → clean missing_image_url error; fal-ai/veo3.1 + prompt-only → dispatches - 6 new tests cover the builder (no config / image-only / full-surface / text-only / unknown provider / registry wiring), all passing - 37/37 in the slice, 134/134 in the broader regression set * test(video_gen/xai): full surface integration tests + cleaner schema Verified end-to-end that the xAI plugin handles every documented mode from PR #10600's surface: text-to-video, image-to-video, reference-images-to-video, video edit, video extend (with and without prompt). All five modes route to the correct xAI endpoint (/videos/generations, /videos/edits, /videos/extensions) with the right payload shape (image / reference_images / video keys), and all five client-side rejections fire before the network: edit-without-prompt, extend-without-video_url, image+refs conflict, >7 references, and duration/aspect_ratio clamping. 15 new integration tests grouped into four classes (endpoint routing, modalities, validation, clamping). httpx is stubbed via a small fake AsyncClient that records POSTs so the tests assert the actual payload the plugin would send to xAI — not just the success/error envelope. Also cleaned up a description redundancy: when a model's operations match the backend's overall set, we no longer print the duplicate 'operations supported by this model' line. xAI's description now reads: Active backend: xAI . model: grok-imagine-video - operations supported by this backend: edit, extend, generate - modalities supported by this backend: image, reference_images, text - aspect_ratio choices: 16:9, 1:1, 2:3, 3:2, 3:4, 4:3, 9:16 - resolution choices: 480p, 720p - duration range: 1-15s - reference_image_urls: up to 7 images Co-authored-by: Jaaneek <Jaaneek@users.noreply.github.com> * feat(video_gen): collapse surface to t2v + i2v, family-based auto-routing Two design changes per Teknium: 1) Drop edit/extend from the tool surface entirely. Only text-to-video and image-to-video remain. The agent sees a clean tool with two modalities; backend-specific quirks like xAI's edit/extend endpoints stay out of the unified schema. 2) FAL: pick a model FAMILY once, the plugin routes between the family's text-to-video and image-to-video endpoints based on whether image_url was passed. Users no longer pick 'fal-ai/veo3.1' AND 'fal-ai/veo3.1/image-to-video' as separate options — they pick 'veo3.1', and the plugin handles the rest. Catalog rewritten as families: veo3.1 fal-ai/veo3.1 / fal-ai/veo3.1/image-to-video pixverse-v6 fal-ai/pixverse/v6/text-to-video / fal-ai/pixverse/v6/image-to-video kling-o3-standard fal-ai/kling-video/o3/standard/text-to-video / fal-ai/kling-video/o3/standard/image-to-video xAI uses a single endpoint (/videos/generations) for both modes, routed by the presence of the 'image' field in the payload — no edit/extend exposure. Schema changes: - VIDEO_GENERATE_SCHEMA: drop operation, drop video_url. Final params: prompt (required), image_url, reference_image_urls, duration, aspect_ratio, resolution, negative_prompt, audio, seed, model. - VideoGenProvider ABC: drop normalize_operation, VALID_OPERATIONS, DEFAULT_OPERATION. capabilities() drops 'operations' key. - success_response: add 'modality' field ('text' | 'image') so the agent and logs can see which endpoint was actually hit. Dynamic schema builder simplified — no operations bullet, no 'switch backends if you need edit/extend' guidance. When the active backend supports both modalities (the common case), description reads: Active backend: FAL . model: pixverse-v6 - supports both text-to-video (omit image_url) and image-to-video (pass image_url) - routes automatically - aspect_ratio choices: 16:9, 9:16, 1:1 - resolution choices: 360p, 540p, 720p, 1080p - duration range: 1-15s - audio: pass audio=true to enable native audio (pricing tier) - negative_prompt: supported Tests: 51 in the video_gen slice, 216 across the broader image+video sweep, all passing. New FAL routing tests prove pixverse-v6 + no image hits text-to-video endpoint, pixverse-v6 + image_url hits image-to-video endpoint, same for veo3.1 and kling-o3-standard. Docs updated: developer-guide page rewrites the 'model families' pattern as a first-class section so external plugin authors know the convention. toolsets-reference and toolsets.py descriptions match the new surface. Co-authored-by: Jaaneek <Jaaneek@users.noreply.github.com> * feat(video_gen/fal): expand catalog to 6 families, cheap + premium tiers Catalog now covers everything Teknium specced from FAL: Cheap tier: ltx-2.3 fal-ai/ltx-2.3-22b/text-to-video / image-to-video pixverse-v6 fal-ai/pixverse/v6/text-to-video / image-to-video Premium tier: veo3.1 fal-ai/veo3.1 / fal-ai/veo3.1/image-to-video seedance-2.0 bytedance/seedance-2.0/text-to-video / image-to-video kling-v3-4k fal-ai/kling-video/v3/4k/text-to-video / image-to-video happy-horse fal-ai/happy-horse/text-to-video / image-to-video DEFAULT_MODEL moved from veo3.1 (premium) to pixverse-v6 (cheap, sane defaults, both modalities) — better first-run UX for users who haven't explicitly picked a model. New family-entry knob: image_param_key. Kling v3 4K's image-to-video endpoint expects start_image_url instead of image_url; declaring image_param_key='start_image_url' on the family lets _build_payload remap correctly. Other families default to plain image_url. Per-family capability flags reflect each model's docs: - LTX 2.3 + Happy Horse: minimal payloads (no duration/aspect/resolution enum exposed by FAL — let endpoint apply defaults) - Seedance: 6 aspect ratios incl 21:9, durations 4-15, audio supported, negative prompts NOT supported per docs - Kling v3 4K: 16:9/9:16/1:1, 3-15s, audio + negative - Veo 3.1: unchanged, 16:9/9:16, 4/6/8s Tests: +5 covering the new families (full catalog, Kling 4K start_image_url remap, Seedance routing, LTX payload minimality, Happy Horse minimality). 56/56 in the slice green. Note: I did NOT add the FAL-hosted xAI Grok-Imagine variant. Hermes already has a direct xAI plugin that talks to xAI's own API; routing the same model through FAL's wrapper would duplicate the surface without adding capabilities. Users on FAL who want Grok-Imagine should use the xAI plugin directly; flag if you want both routes available. * test(video_gen): tool-surface routing matrix — every model x modality End-to-end matrix test driven through _handle_video_generate() — the actual function the agent's video_generate tool call lands in. Writes config.yaml, invokes the registered handler with a raw args dict, then asserts the outbound HTTP/SDK call hit the right endpoint with the right payload shape. Parametrized over FAL_FAMILIES.keys() so the matrix auto-discovers new families as they're added (add a family to FAL_FAMILIES and you get both modalities tested for free). Coverage: - All 6 FAL families x {text-only, text+image} = 12 cases - xAI x {text-only, text+image} = 2 cases - tool-level model= arg overrides config = 2 cases For each case, verifies: - result['success'] is True - result['modality'] matches input shape ('text' if no image_url, 'image' otherwise) - outbound endpoint URL matches the family's text_endpoint or image_endpoint - text-only payloads carry no image-shaped keys - text+image payloads carry the family's image key (image_url for most, start_image_url for kling-v3-4k, wrapped 'image' object for xAI) All 16 cases passing. Confirms the tool surface routes every (provider, model, modality) combination correctly with zero leakage. * feat(video_gen): keep video_gen out of first-run setup, surface in status Two changes: 1. video_gen joins _DEFAULT_OFF_TOOLSETS, so it is NOT pre-selected in the first-run toolset checklist. Video gen is niche, paid, and slow — most users don't want it nagging them during initial setup. Anyone who wants it opts in via 'hermes tools' -> Video Generation, which already routes to the provider+model picker. 2. The 'hermes setup' status panel learns about video_gen — but only shows the row when a plugin reports available. Users without FAL_KEY/XAI_API_KEY see nothing about video gen; users with one of those keys see 'Video Generation (FAL) ✓' as confirmation it's wired. Verified live: - Fresh install (no creds): zero video_gen mentions in wizard. - With FAL_KEY: status row appears with active backend name. - 160/160 in the setup + tools_config + video_gen test slice. Rationale: image_gen is on by default because it's a featured creative tool used in casual chat (telegrams, etc). Video gen is heavier — long wait, paid per-second pricing. Default-off matches user intent better. --------- Co-authored-by: Jaaneek <Jaaneek@users.noreply.github.com>
2026-05-13 16:39:41 -07:00
| Register a video-generation backend | `ctx.register_video_gen_provider(provider)` — see [Video Generation Provider Plugins](/docs/developer-guide/video-gen-provider-plugin) |
docs: pluggable surfaces coverage — model-provider guide, full plugin map, opt-in fix (#20749) * docs(providers): add model-provider-plugin authoring guide + fix stale refs New docs: - website/docs/developer-guide/model-provider-plugin.md — full authoring guide (directory layout, minimal example, ProviderProfile fields, overridable hooks, user overrides, api_mode selection, auth types, testing, pip distribution) - Wired into website/sidebars.ts under 'Extending' - Cross-references added in: - guides/build-a-hermes-plugin.md (tip block) - developer-guide/adding-providers.md - developer-guide/provider-runtime.md User guide: - user-guide/features/plugins.md: Plugin types table grows from 3 to 4 with 'Model providers' row Stale comment cleanup (providers/*.py → plugins/model-providers/<name>/): - hermes_cli/main.py:_is_profile_api_key_provider docstring - hermes_cli/doctor.py:_build_apikey_providers_list docstring - hermes_cli/auth.py: PROVIDER_REGISTRY + alias auto-extension comments - hermes_cli/models.py: CANONICAL_PROVIDERS auto-extension comment AGENTS.md: - Project-structure tree: added plugins/model-providers/ row - New section: 'Model-provider plugins' explaining discovery, override semantics, PluginManager integration, kind auto-coerce heuristic Verified: docusaurus build succeeds, new page renders, all 3 cross-links resolve. 347/347 targeted tests pass (tests/providers/, tests/hermes_cli/test_plugins.py, tests/hermes_cli/test_runtime_provider_resolution.py, tests/run_agent/test_provider_parity.py). * docs(plugins): add 'pluggable interfaces at a glance' maps to plugins.md + build-a-hermes-plugin Devs landing on either the user-guide plugin page or the build-a-plugin guide now get an upfront table of every distinct pluggable surface with a link to the right authoring doc. Previously they'd have to read the full general-plugin guide to discover that model providers / platforms / memory / context engines are separate systems. user-guide/features/plugins.md: - New 'Pluggable interfaces — where to go for each' section below the existing 4-kinds table - 10 rows covering every register_* surface (tool, hook, slash command, CLI subcommand, skill, model provider, platform, memory, context engine, image-gen) - Explicit note: TTS/STT are NOT plugin-extensible yet — documented with a pointer to the current config.yaml 'command providers' pattern and a note that register_tts_provider()/register_stt_provider() may come later guides/build-a-hermes-plugin.md: - New :::info 'Not sure which guide you need?' map at the top so devs see all pluggable interfaces before investing in this 737-line general-plugin walkthrough - Existing bottom :::tip expanded to include platform adapters alongside model/memory/context plugins Verified: - All 8 cross-doc links in the new plugins.md table resolve in a docusaurus build (SUCCESS, no new broken links) - TTS link corrected (features/voice → features/tts; latter exists) - Pre-existing broken links/anchors (cron-script-only, llms.txt, adding-platform-adapters#step-by-step-checklist) are unchanged * docs(plugins): correct TTS/STT pluggability \u2014 they ARE plugins (command-providers) Previous commit incorrectly said TTS/STT 'aren't plugin-extensible'. They are, via the config-driven command-provider pattern \u2014 any CLI that reads text and writes audio (or vice versa for STT) is automatically a plugin with zero Python. The tts.md docs cover this extensively and I missed it. plugins.md: - TTS row: 'Config-driven (not a Python plugin)', points at tts.md#custom-command-providers - STT row: points at tts.md#voice-message-transcription-stt (STT docs live in tts.md despite the filename) - Expanded note: TTS/STT use config-driven shell-command templates as their plugin surface (full tts.providers.<name> registry for TTS; HERMES_LOCAL_STT_COMMAND escape hatch for STT) - Any CLI that reads/writes files is automatically a plugin \u2014 no Python register_* API needed - Future register_tts_provider()/register_stt_provider() hooks mentioned as nice-to-have for SDK/streaming cases, not as the primary story build-a-hermes-plugin.md: - Same map update: TTS/STT rows explicit, footer note corrected Verified: - tts.md anchors (custom-command-providers, voice-message-transcription-stt) exist and resolve in docusaurus build (SUCCESS, no new broken links) * docs(plugins): expand pluggable interfaces table with MCP / event hooks / shell hooks / skill taps Broadened the scope beyond Python register_* hooks. Hermes has MULTIPLE plugin-style extension surfaces; they're now all in one table instead of being scattered across feature docs. Added rows for: - **MCP servers** — config.yaml mcp_servers.<name> auto-registers external tools from any MCP server. Huge extensibility surface, previously not linked from the plugin map. - **Gateway event hooks** — drop HOOK.yaml + handler.py into ~/.hermes/hooks/<name>/ to fire on gateway:startup, session:*, agent:*, command:* events. Separate from Python plugin hooks. - **Shell hooks** — hooks: block in config.yaml runs shell commands on events (notifications, auditing, etc.). - **Skill sources (taps)** — hermes skills tap add <repo> to pull in new skill registries beyond the built-in sources. Both docs updated: - user-guide/features/plugins.md: table column renamed to 'How' (mixes Python API + config-driven + drop-in-dir surfaces accurately) - guides/build-a-hermes-plugin.md: :::info map at top mirrors the new surfaces with a forward-link to the consolidated table Note block rewritten: instead of singling out TTS/STT as the 'different style' exception, now honestly describes that Hermes deliberately supports three plugin styles — Python APIs, config-driven commands, and drop-in manifest directories — and devs should pick the one that fits their integration. Not included (considered and rejected): - Transport layer (register_transport) — internal, not user-facing - Tool-call parsers — internal, VLLM phase-2 thing - Cloud browser providers — hardcoded registry, not drop-in yet - Terminal backends — hardcoded if/elif, not drop-in yet - Skill sources (the ABC) — hardcoded list, only taps are user-extensible Verified: - All 5 new anchors resolve (gateway-event-hooks, shell-hooks, skills-hub, custom-command-providers, voice-message-transcription-stt) - Docusaurus build SUCCESS, zero new broken links - Same 3 pre-existing broken links on main (cron-script-only, llms.txt, adding-platform-adapters#step-by-step-checklist) * docs(plugins): cover every pluggable surface in both the overview and how-to Both plugins.md and build-a-hermes-plugin.md now cover every extension surface end-to-end \u2014 general plugin APIs, specialized plugin types, config-driven surfaces \u2014 with concrete authoring patterns for each. plugins.md: - 'What plugins can do' table grows from 9 rows (general ctx.register_* only) to 14 rows covering register_platform, register_image_gen_provider, register_context_engine, MemoryProvider subclass, register_provider (model). Each row links to its full authoring guide. - New 'Plugin sub-categories' section under Plugin Discovery explains how plugins/platforms/, plugins/image_gen/, plugins/memory/, plugins/context_engine/, plugins/model-providers/ are routed to different loaders \u2014 PluginManager vs the per-category own-loader systems. - Explicit mention of user-override semantics at ~/.hermes/plugins/model-providers/ and ~/.hermes/plugins/memory/. build-a-hermes-plugin.md: - New '## Specialized plugin types' section (5 sub-sections): - Model provider plugins \u2014 ProviderProfile + plugin.yaml example, auto-wiring summary, link to full guide - Platform plugins \u2014 BasePlatformAdapter + register_platform() skeleton - Memory provider plugins \u2014 MemoryProvider subclass example - Context engine plugins \u2014 ContextEngine subclass example - Image-generation backends \u2014 ImageGenProvider + kind: backend example - New '## Non-Python extension surfaces' section (5 sub-sections): - MCP servers \u2014 config.yaml mcp_servers.<name> example - Gateway event hooks \u2014 HOOK.yaml + handler.py example - Shell hooks \u2014 hooks: block in config.yaml example - Skill sources (taps) \u2014 hermes skills tap add example - TTS / STT command templates \u2014 tts.providers.<name> with type: command - Distribute via pip / NixOS promoted from ### to ## (they were orphaned after the reorganization) Each specialized / non-Python section has a concrete, copy-pasteable example plus a 'Full guide:' link to the authoritative doc. Devs arriving at the build-a-hermes-plugin guide now see every extension surface at their disposal, not just the general tool/hook/slash-command surface. Verified: - Docusaurus build SUCCESS, zero new broken links - All new cross-links (developer-guide/model-provider-plugin, adding-platform-adapters, memory-provider-plugin, context-engine-plugin, user-guide/features/mcp, skills#skills-hub, hooks#gateway-event-hooks, hooks#shell-hooks, tts#custom-command-providers, tts#voice-message-transcription-stt) resolve - Same 3 pre-existing broken links on main (cron-script-only, llms.txt, adding-platform-adapters#step-by-step-checklist) * docs(plugins): fix opt-in inconsistency — not every plugin is gated The 'Every plugin is disabled by default' statement was wrong. Several plugin categories intentionally bypass plugins.enabled: - Bundled platform plugins (IRC, Teams) auto-load so shipped gateway channels are available out of the box. Activation per channel is via gateway.platforms.<name>.enabled. - Bundled backends (plugins/image_gen/*) auto-load so the default backend 'just works'. Selection via <category>.provider config. - Memory providers are all discovered; one is active via memory.provider. - Context engines are all discovered; one is active via context.engine. - Model providers: all 33 discovered at first get_provider_profile(); user picks via --provider / config. The plugins.enabled allow-list specifically gates: - Standalone plugins (general tools/hooks/slash commands) - User-installed backends - User-installed platforms (third-party gateway adapters) - Pip entry-point backends Which matches the actual code in hermes_cli/plugins.py:737 where the bundled+backend/platform check bypasses the allow-list. Rewrote '## Plugins are opt-in' to: - Retitle to 'Plugins are opt-in (with a few exceptions)' - Narrow opening claim to 'General plugins and user-installed backends are disabled by default' - Added 'What the allow-list does NOT gate' subsection with a full table of which bypass the gate and how they're activated instead - Fixed migration section wording (bundled platform/backend plugins never needed grandfathering) Verified: docusaurus build SUCCESS, zero new broken links.
2026-05-06 07:24:42 -07:00
| Register a context-compression engine | `ctx.register_context_engine(engine)` — see [Context Engine Plugins](/docs/developer-guide/context-engine-plugin) |
| Register a memory backend | Subclass `MemoryProvider` in `plugins/memory/<name>/__init__.py` — see [Memory Provider Plugins](/docs/developer-guide/memory-provider-plugin) (uses a separate discovery system) |
feat(plugins): run any LLM call from inside a plugin via ctx.llm (#23194) * feat(plugins): host-owned LLM access via ctx.llm Plugins can now ask the host to run a one-shot chat or structured completion against the user's active model and auth, without ever seeing an OAuth token or API key. Closes the gap where plugins that needed bounded structured inference (receipts, CRM extraction, support classification) had to either bring their own provider keys or register a tool the agent had to call. New surface on PluginContext: - ctx.llm.complete(messages, ...) - ctx.llm.complete_structured(instructions, input, json_schema, ...) - async siblings ctx.llm.acomplete / acomplete_structured Backed by the existing auxiliary_client.call_llm pipeline — every provider, fallback chain, vision routing, and timeout policy Hermes already supports applies automatically. Trust gate (fail-closed by default): - plugins.entries.<id>.llm.allow_model_override - plugins.entries.<id>.llm.allowed_models (allowlist; '*' = any) - plugins.entries.<id>.llm.allow_agent_id_override - plugins.entries.<id>.llm.allow_profile_override Embedded model@profile shorthand goes through the same gate as explicit profile=, so it can't bypass the auth-profile policy. Conflicting explicit and embedded profiles fail closed. Also lands: - plugins/plugin-llm-example/ — reference plugin that registers /receipt-extract, demonstrating image+text structured input, jsonschema validation, and the trust-gate config. - website/docs/developer-guide/plugin-llm-access.md — full API docs. - 45 unit tests covering trust gates, JSON parsing, schema validation, image encoding, async surface, and config loading. Validation: - 2628 tests pass in tests/agent/ - E2E: bundled plugin loaded with isolated HERMES_HOME, slash command produced parsed JSON via stubbed call_llm - response_format extra_body wired correctly for both json_object and json_schema modes * docs(plugin-llm): rewrite quickstart and framing The quickstart now uses a meeting-notes-to-tasks example instead of a receipt extractor, and the page leads with hook-time / gateway pre-filter / scheduled-job framing rather than the OpenClaw KB/support/CRM/finance/migration enumeration that the original upstream PR used. Receipt example moved to a separate worked example link so the docs page itself doesn't echo any of the upstream framing. Also clarifies where ctx.llm fits in the broader plugin surface (table comparing register_tool / register_platform / register_hook / etc.) and what makes this lane different from auxiliary_client internals. No code change. * docs(plugin-llm): reframe as any LLM call, not just structured output The original draft leaned heavily on complete_structured() and made the chat lane (complete() / acomplete()) feel like a footnote. Restructure so: - The page title and description say 'any LLM call.' - The lead shows BOTH a plain chat call (error rewriter) AND a structured call (triage scorer) up top. - Quick start has two complete plugin examples — /tldr (chat) and /paste-to-tasks (structured). - New 'When to use which' table for choosing complete() vs complete_structured() vs the async siblings. - Trust-gate sections explicitly note 'all four methods,' and the request-shaping list calls out chat-only fields (messages) and structured-only fields (instructions, input, json_schema) alongside each other. - The 'Where this fits' section now says 'for any reason, structured or not.' The receipt-extractor reference plugin still exists under plugins/plugin-llm-example/ — but the docs page no longer treats it as the canonical surface example. It's now described as 'a third worked example, this time with image input.' No code change. * feat(plugin-llm): split provider/model into independent explicit kwargs The first cut accepted a single 'provider/model' slug on every method and split it internally. That looked clean but broke under live test: the model-override path tried to use the slug's vendor prefix as a literal Hermes provider id, which silently switched the user off their aggregator (e.g. plugin asks for 'openai/gpt-4o-mini' on a user who routes through OpenRouter — host attempted to call the 'openai' provider directly, failed because OPENAI_API_KEY wasn't set). New shape mirrors the host's main config: ctx.llm.complete( messages=[...], provider='openrouter', # gated, optional model='openai/gpt-4o-mini', # gated, optional profile='work', # gated, optional ... ) Each is independently gated by its own allow_*_override flag. Granting model-override does NOT auto-grant provider-override. Allowlists are now per-axis (allowed_providers, allowed_models) matched literally against whatever string the plugin sends. Dropped 'model@profile' embedded-suffix shorthand entirely. Hermes doesn't use that pattern anywhere else; profile= is its own kwarg. Live E2E (against real OpenRouter via Teknium's config) confirms: - zero-config call works - default-deny blocks each override with a helpful error - model-only override stays on user's active provider (the bug) - provider+model override switches cleanly - allowlist refuses non-listed entries - structured output round-trip parses + schema-validates Tests: 49 cases (up from 45); all green. Docs updated to match the new shape, including a 'most plugins never need this section' callout on the trust-gate config block. * fix+cleanup(plugin-llm): real attribution, hook-mode coverage, move example out of core Three integration fixes for the ctx.llm surface: 1. Attribution bug — result.provider and result.model now reflect what call_llm actually used, not placeholder fallbacks ('auto', 'default'). New _resolve_attribution() helper: - explicit overrides win (what the call targeted) - response.model wins for the recorded model (provider canonicalisation: 'gpt-4o' → 'gpt-4o-2024-08-06' etc.) - falls back to _read_main_provider() / _read_main_model() when no override is set, so audit logs reflect the user's active main provider/model - 'auto' / 'default' only when EVERYTHING is empty Live verified: zero-config call now records provider='openrouter', model='anthropic/claude-4.7-opus-20260416' instead of provider='auto', model='default'. 2. Hook-mode coverage — TestHookMode confirms ctx.llm.complete works from inside a registered post_tool_call callback. The docs page promised hook integration; now there's a test that exercises the lazy-import path through the real invoke_hook machinery. Two cases: traceback-rewrite hook with conditional ctx.llm.complete, and minimal hook regression for the sync-hook + sync-llm path. 3. Reference plugin moved out of core. plugins/plugin-llm-example/ is gone from hermes-agent — it now lives in the new NousResearch/hermes-example-plugins companion repo. The docs page links there. Hermes' bundled plugins should be plugins users actually run; reference / docs-companion plugins live externally. Test count: 56 (up from 49). Wider sweep on tests/hermes_cli/ + tests/gateway/ + tests/tools/ + tests/agent/ shows 16770 passing; the 12 failures are all pre-existing on origin/main (verified by stashing this branch's changes and re-running) — kanban-boards, delegate-task, gateway-restart, tts-routing — none touch the plugin_llm surface. * chore(plugins): move all example plugins to companion repo Reference / docs-companion plugins now live exclusively in NousResearch/hermes-example-plugins, not bundled with the core repo: - example-dashboard - strike-freedom-cockpit A new fourth example, plugin-llm-async-example, was added to that repo demonstrating ctx.llm's async surface (acomplete()) with asyncio.gather() — registers /translate <lang>: <text> which fires forward translation + sentiment classifier in parallel, then a back-translation for QA. Live-tested at 2.5s for three real provider round-trips (would be ~5-6s sequential). Docs updated: - developer-guide/plugin-llm-access.md links both sync and async examples in the Reference section - user-guide/features/extending-the-dashboard.md repoints both demo sections to the companion repo with corrected install paths - user-guide/features/built-in-plugins.md drops the two demo rows - AGENTS.md notes that example plugins live in the companion repo Net: hermes-agent's plugins/ directory now contains only plugins users actually run (memory providers, dashboard tabs that ship real features, the disk-cleanup hook, platform adapters). All four demo / reference plugins live externally where they can be cloned on demand instead of inflating the core install.
2026-05-10 07:09:28 -07:00
| Run a host-owned LLM call | `ctx.llm.complete(...)` / `ctx.llm.complete_structured(...)` — borrow the user's active model + auth for a one-shot completion with optional JSON schema validation. See [Plugin LLM Access](/docs/developer-guide/plugin-llm-access) |
docs: pluggable surfaces coverage — model-provider guide, full plugin map, opt-in fix (#20749) * docs(providers): add model-provider-plugin authoring guide + fix stale refs New docs: - website/docs/developer-guide/model-provider-plugin.md — full authoring guide (directory layout, minimal example, ProviderProfile fields, overridable hooks, user overrides, api_mode selection, auth types, testing, pip distribution) - Wired into website/sidebars.ts under 'Extending' - Cross-references added in: - guides/build-a-hermes-plugin.md (tip block) - developer-guide/adding-providers.md - developer-guide/provider-runtime.md User guide: - user-guide/features/plugins.md: Plugin types table grows from 3 to 4 with 'Model providers' row Stale comment cleanup (providers/*.py → plugins/model-providers/<name>/): - hermes_cli/main.py:_is_profile_api_key_provider docstring - hermes_cli/doctor.py:_build_apikey_providers_list docstring - hermes_cli/auth.py: PROVIDER_REGISTRY + alias auto-extension comments - hermes_cli/models.py: CANONICAL_PROVIDERS auto-extension comment AGENTS.md: - Project-structure tree: added plugins/model-providers/ row - New section: 'Model-provider plugins' explaining discovery, override semantics, PluginManager integration, kind auto-coerce heuristic Verified: docusaurus build succeeds, new page renders, all 3 cross-links resolve. 347/347 targeted tests pass (tests/providers/, tests/hermes_cli/test_plugins.py, tests/hermes_cli/test_runtime_provider_resolution.py, tests/run_agent/test_provider_parity.py). * docs(plugins): add 'pluggable interfaces at a glance' maps to plugins.md + build-a-hermes-plugin Devs landing on either the user-guide plugin page or the build-a-plugin guide now get an upfront table of every distinct pluggable surface with a link to the right authoring doc. Previously they'd have to read the full general-plugin guide to discover that model providers / platforms / memory / context engines are separate systems. user-guide/features/plugins.md: - New 'Pluggable interfaces — where to go for each' section below the existing 4-kinds table - 10 rows covering every register_* surface (tool, hook, slash command, CLI subcommand, skill, model provider, platform, memory, context engine, image-gen) - Explicit note: TTS/STT are NOT plugin-extensible yet — documented with a pointer to the current config.yaml 'command providers' pattern and a note that register_tts_provider()/register_stt_provider() may come later guides/build-a-hermes-plugin.md: - New :::info 'Not sure which guide you need?' map at the top so devs see all pluggable interfaces before investing in this 737-line general-plugin walkthrough - Existing bottom :::tip expanded to include platform adapters alongside model/memory/context plugins Verified: - All 8 cross-doc links in the new plugins.md table resolve in a docusaurus build (SUCCESS, no new broken links) - TTS link corrected (features/voice → features/tts; latter exists) - Pre-existing broken links/anchors (cron-script-only, llms.txt, adding-platform-adapters#step-by-step-checklist) are unchanged * docs(plugins): correct TTS/STT pluggability \u2014 they ARE plugins (command-providers) Previous commit incorrectly said TTS/STT 'aren't plugin-extensible'. They are, via the config-driven command-provider pattern \u2014 any CLI that reads text and writes audio (or vice versa for STT) is automatically a plugin with zero Python. The tts.md docs cover this extensively and I missed it. plugins.md: - TTS row: 'Config-driven (not a Python plugin)', points at tts.md#custom-command-providers - STT row: points at tts.md#voice-message-transcription-stt (STT docs live in tts.md despite the filename) - Expanded note: TTS/STT use config-driven shell-command templates as their plugin surface (full tts.providers.<name> registry for TTS; HERMES_LOCAL_STT_COMMAND escape hatch for STT) - Any CLI that reads/writes files is automatically a plugin \u2014 no Python register_* API needed - Future register_tts_provider()/register_stt_provider() hooks mentioned as nice-to-have for SDK/streaming cases, not as the primary story build-a-hermes-plugin.md: - Same map update: TTS/STT rows explicit, footer note corrected Verified: - tts.md anchors (custom-command-providers, voice-message-transcription-stt) exist and resolve in docusaurus build (SUCCESS, no new broken links) * docs(plugins): expand pluggable interfaces table with MCP / event hooks / shell hooks / skill taps Broadened the scope beyond Python register_* hooks. Hermes has MULTIPLE plugin-style extension surfaces; they're now all in one table instead of being scattered across feature docs. Added rows for: - **MCP servers** — config.yaml mcp_servers.<name> auto-registers external tools from any MCP server. Huge extensibility surface, previously not linked from the plugin map. - **Gateway event hooks** — drop HOOK.yaml + handler.py into ~/.hermes/hooks/<name>/ to fire on gateway:startup, session:*, agent:*, command:* events. Separate from Python plugin hooks. - **Shell hooks** — hooks: block in config.yaml runs shell commands on events (notifications, auditing, etc.). - **Skill sources (taps)** — hermes skills tap add <repo> to pull in new skill registries beyond the built-in sources. Both docs updated: - user-guide/features/plugins.md: table column renamed to 'How' (mixes Python API + config-driven + drop-in-dir surfaces accurately) - guides/build-a-hermes-plugin.md: :::info map at top mirrors the new surfaces with a forward-link to the consolidated table Note block rewritten: instead of singling out TTS/STT as the 'different style' exception, now honestly describes that Hermes deliberately supports three plugin styles — Python APIs, config-driven commands, and drop-in manifest directories — and devs should pick the one that fits their integration. Not included (considered and rejected): - Transport layer (register_transport) — internal, not user-facing - Tool-call parsers — internal, VLLM phase-2 thing - Cloud browser providers — hardcoded registry, not drop-in yet - Terminal backends — hardcoded if/elif, not drop-in yet - Skill sources (the ABC) — hardcoded list, only taps are user-extensible Verified: - All 5 new anchors resolve (gateway-event-hooks, shell-hooks, skills-hub, custom-command-providers, voice-message-transcription-stt) - Docusaurus build SUCCESS, zero new broken links - Same 3 pre-existing broken links on main (cron-script-only, llms.txt, adding-platform-adapters#step-by-step-checklist) * docs(plugins): cover every pluggable surface in both the overview and how-to Both plugins.md and build-a-hermes-plugin.md now cover every extension surface end-to-end \u2014 general plugin APIs, specialized plugin types, config-driven surfaces \u2014 with concrete authoring patterns for each. plugins.md: - 'What plugins can do' table grows from 9 rows (general ctx.register_* only) to 14 rows covering register_platform, register_image_gen_provider, register_context_engine, MemoryProvider subclass, register_provider (model). Each row links to its full authoring guide. - New 'Plugin sub-categories' section under Plugin Discovery explains how plugins/platforms/, plugins/image_gen/, plugins/memory/, plugins/context_engine/, plugins/model-providers/ are routed to different loaders \u2014 PluginManager vs the per-category own-loader systems. - Explicit mention of user-override semantics at ~/.hermes/plugins/model-providers/ and ~/.hermes/plugins/memory/. build-a-hermes-plugin.md: - New '## Specialized plugin types' section (5 sub-sections): - Model provider plugins \u2014 ProviderProfile + plugin.yaml example, auto-wiring summary, link to full guide - Platform plugins \u2014 BasePlatformAdapter + register_platform() skeleton - Memory provider plugins \u2014 MemoryProvider subclass example - Context engine plugins \u2014 ContextEngine subclass example - Image-generation backends \u2014 ImageGenProvider + kind: backend example - New '## Non-Python extension surfaces' section (5 sub-sections): - MCP servers \u2014 config.yaml mcp_servers.<name> example - Gateway event hooks \u2014 HOOK.yaml + handler.py example - Shell hooks \u2014 hooks: block in config.yaml example - Skill sources (taps) \u2014 hermes skills tap add example - TTS / STT command templates \u2014 tts.providers.<name> with type: command - Distribute via pip / NixOS promoted from ### to ## (they were orphaned after the reorganization) Each specialized / non-Python section has a concrete, copy-pasteable example plus a 'Full guide:' link to the authoritative doc. Devs arriving at the build-a-hermes-plugin guide now see every extension surface at their disposal, not just the general tool/hook/slash-command surface. Verified: - Docusaurus build SUCCESS, zero new broken links - All new cross-links (developer-guide/model-provider-plugin, adding-platform-adapters, memory-provider-plugin, context-engine-plugin, user-guide/features/mcp, skills#skills-hub, hooks#gateway-event-hooks, hooks#shell-hooks, tts#custom-command-providers, tts#voice-message-transcription-stt) resolve - Same 3 pre-existing broken links on main (cron-script-only, llms.txt, adding-platform-adapters#step-by-step-checklist) * docs(plugins): fix opt-in inconsistency — not every plugin is gated The 'Every plugin is disabled by default' statement was wrong. Several plugin categories intentionally bypass plugins.enabled: - Bundled platform plugins (IRC, Teams) auto-load so shipped gateway channels are available out of the box. Activation per channel is via gateway.platforms.<name>.enabled. - Bundled backends (plugins/image_gen/*) auto-load so the default backend 'just works'. Selection via <category>.provider config. - Memory providers are all discovered; one is active via memory.provider. - Context engines are all discovered; one is active via context.engine. - Model providers: all 33 discovered at first get_provider_profile(); user picks via --provider / config. The plugins.enabled allow-list specifically gates: - Standalone plugins (general tools/hooks/slash commands) - User-installed backends - User-installed platforms (third-party gateway adapters) - Pip entry-point backends Which matches the actual code in hermes_cli/plugins.py:737 where the bundled+backend/platform check bypasses the allow-list. Rewrote '## Plugins are opt-in' to: - Retitle to 'Plugins are opt-in (with a few exceptions)' - Narrow opening claim to 'General plugins and user-installed backends are disabled by default' - Added 'What the allow-list does NOT gate' subsection with a full table of which bypass the gate and how they're activated instead - Fixed migration section wording (bundled platform/backend plugins never needed grandfathering) Verified: docusaurus build SUCCESS, zero new broken links.
2026-05-06 07:24:42 -07:00
| Register an inference backend (LLM provider) | `register_provider(ProviderProfile(...))` in `plugins/model-providers/<name>/__init__.py` — see [Model Provider Plugins](/docs/developer-guide/model-provider-plugin) (uses a separate discovery system) |
## Plugin discovery
| Source | Path | Use case |
|--------|------|----------|
| Bundled | `<repo>/plugins/` | Ships with Hermes — see [Built-in Plugins](/docs/user-guide/features/built-in-plugins) |
| User | `~/.hermes/plugins/` | Personal plugins |
| Project | `.hermes/plugins/` | Project-specific plugins (requires `HERMES_ENABLE_PROJECT_PLUGINS=true`) |
| pip | `hermes_agent.plugins` entry_points | Distributed packages |
feat(nix): declarative plugin installation for NixOS module (#15953) * feat(nix): parameterize dependency-groups in python.nix * refactor(nix): extract package to callPackage-able hermes-agent.nix Makes the package overridable via .override{} and adds extraPythonPackages parameter for PYTHONPATH injection. Includes build-time collision check using PEP 503 name canonicalization. * feat(nix): add overlay for external NixOS consumption External flakes can now add overlays = [ inputs.hermes-agent.overlays.default ] to get pkgs.hermes-agent with full .override support. * test(nix): add check for extraPythonPackages PYTHONPATH injection Verifies wrapper has PYTHONPATH when extras provided, and base package has no PYTHONPATH without extras. * feat(nix): add extraPlugins option for directory-based plugins Symlinks plugin packages into HERMES_HOME/plugins/ at activation time. Validates plugin.yaml presence. Asserts unique plugin names at eval time. Hermes discovers them automatically via its directory scan. * feat(nix): add extraPythonPackages option for entry-point plugins Overrides the hermes package with PYTHONPATH injection when extraPythonPackages is non-empty. Plugin .dist-info directories become visible to importlib.metadata for entry-point discovery. Works in both native systemd and container modes. * docs: add NixOS declarative plugin installation to nix-setup, plugins, and build-a-plugin guides - nix-setup.md: new Plugins section with extraPlugins/extraPythonPackages examples, overlay usage, collision checking note, options reference rows - plugins.md: Nix row in discovery table, NixOS declarative plugins section - build-a-hermes-plugin.md: Distribute for NixOS section after pip section * fix: address review feedback — remove unrelated umask, fix fetchFromGitHub naming, simplify checks - Remove accidentally introduced umask/migration changes (unrelated to plugins) - Add pluginName helper, fix fetchFromGitHub producing name='source' - Show name= in extraPlugins example docs - Simplify checks.nix: use hermes-agent.override instead of re-callPackage - Fix fragile grep shell logic in checks * refactor: address simplify feedback — lib.getName, drop unused inputs', Python list for extras - Use lib.getName instead of custom pluginName helper - Drop unused inputs' from checks.nix perSystem args - Pass extraPythonPackages as Python list literal instead of colon-split string * fix: walk propagatedBuildInputs for plugin PYTHONPATH and collision check Uses python312.pkgs.requiredPythonModules to resolve the full transitive closure of extraPythonPackages. Without this, a plugin with third-party deps (e.g. requests) would fail at runtime if those deps weren't already in the sealed uv2nix venv. The collision check now also scans the full closure, catching transitive conflicts. * cleanup: fold plugins into subdir loop, use find for symlink cleanup, inline lib.getName - Add 'plugins' to the existing cron/sessions/logs/memories subdir loop instead of a separate mkdir/chown/chmod block - Replace fragile for-glob with find -delete for stale symlink cleanup - Inline lib.getName at both call sites, remove pluginName wrapper
2026-04-28 00:18:32 +05:30
| Nix | `services.hermes-agent.extraPlugins` / `extraPythonPackages` | NixOS declarative installs — see [Nix Setup](/docs/getting-started/nix-setup#plugins) |
feat(plugins): make all plugins opt-in by default Plugins now require explicit consent to load. Discovery still finds every plugin — user-installed, bundled, and pip — so they all show up in `hermes plugins` and `/plugins`, but the loader only instantiates plugins whose name appears in `plugins.enabled` in config.yaml. This removes the previous ambient-execution risk where a newly-installed or bundled plugin could register hooks, tools, and commands on first run without the user opting in. The three-state model is now explicit: enabled — in plugins.enabled, loads on next session disabled — in plugins.disabled, never loads (wins over enabled) not enabled — discovered but never opted in (default for new installs) `hermes plugins install <repo>` prompts "Enable 'name' now? [y/N]" (defaults to no). New `--enable` / `--no-enable` flags skip the prompt for scripted installs. `hermes plugins enable/disable` manage both lists so a disabled plugin stays explicitly off even if something later adds it to enabled. Config migration (schema v20 → v21): existing user plugins already installed under ~/.hermes/plugins/ (minus anything in plugins.disabled) are auto-grandfathered into plugins.enabled so upgrades don't silently break working setups. Bundled plugins are NOT grandfathered — even existing users have to opt in explicitly. Also: HERMES_DISABLE_BUNDLED_PLUGINS env var removed (redundant with opt-in default), cmd_list now shows bundled + user plugins together with their three-state status, interactive UI tags bundled entries [bundled], docs updated across plugins.md and built-in-plugins.md. Validation: 442 plugin/config tests pass. E2E: fresh install discovers disk-cleanup but does not load it; `hermes plugins enable disk-cleanup` activates hooks; migration grandfathers existing user plugins correctly while leaving bundled plugins off.
2026-04-20 04:40:17 -07:00
Later sources override earlier ones on name collision, so a user plugin with the same name as a bundled plugin replaces it.
docs: pluggable surfaces coverage — model-provider guide, full plugin map, opt-in fix (#20749) * docs(providers): add model-provider-plugin authoring guide + fix stale refs New docs: - website/docs/developer-guide/model-provider-plugin.md — full authoring guide (directory layout, minimal example, ProviderProfile fields, overridable hooks, user overrides, api_mode selection, auth types, testing, pip distribution) - Wired into website/sidebars.ts under 'Extending' - Cross-references added in: - guides/build-a-hermes-plugin.md (tip block) - developer-guide/adding-providers.md - developer-guide/provider-runtime.md User guide: - user-guide/features/plugins.md: Plugin types table grows from 3 to 4 with 'Model providers' row Stale comment cleanup (providers/*.py → plugins/model-providers/<name>/): - hermes_cli/main.py:_is_profile_api_key_provider docstring - hermes_cli/doctor.py:_build_apikey_providers_list docstring - hermes_cli/auth.py: PROVIDER_REGISTRY + alias auto-extension comments - hermes_cli/models.py: CANONICAL_PROVIDERS auto-extension comment AGENTS.md: - Project-structure tree: added plugins/model-providers/ row - New section: 'Model-provider plugins' explaining discovery, override semantics, PluginManager integration, kind auto-coerce heuristic Verified: docusaurus build succeeds, new page renders, all 3 cross-links resolve. 347/347 targeted tests pass (tests/providers/, tests/hermes_cli/test_plugins.py, tests/hermes_cli/test_runtime_provider_resolution.py, tests/run_agent/test_provider_parity.py). * docs(plugins): add 'pluggable interfaces at a glance' maps to plugins.md + build-a-hermes-plugin Devs landing on either the user-guide plugin page or the build-a-plugin guide now get an upfront table of every distinct pluggable surface with a link to the right authoring doc. Previously they'd have to read the full general-plugin guide to discover that model providers / platforms / memory / context engines are separate systems. user-guide/features/plugins.md: - New 'Pluggable interfaces — where to go for each' section below the existing 4-kinds table - 10 rows covering every register_* surface (tool, hook, slash command, CLI subcommand, skill, model provider, platform, memory, context engine, image-gen) - Explicit note: TTS/STT are NOT plugin-extensible yet — documented with a pointer to the current config.yaml 'command providers' pattern and a note that register_tts_provider()/register_stt_provider() may come later guides/build-a-hermes-plugin.md: - New :::info 'Not sure which guide you need?' map at the top so devs see all pluggable interfaces before investing in this 737-line general-plugin walkthrough - Existing bottom :::tip expanded to include platform adapters alongside model/memory/context plugins Verified: - All 8 cross-doc links in the new plugins.md table resolve in a docusaurus build (SUCCESS, no new broken links) - TTS link corrected (features/voice → features/tts; latter exists) - Pre-existing broken links/anchors (cron-script-only, llms.txt, adding-platform-adapters#step-by-step-checklist) are unchanged * docs(plugins): correct TTS/STT pluggability \u2014 they ARE plugins (command-providers) Previous commit incorrectly said TTS/STT 'aren't plugin-extensible'. They are, via the config-driven command-provider pattern \u2014 any CLI that reads text and writes audio (or vice versa for STT) is automatically a plugin with zero Python. The tts.md docs cover this extensively and I missed it. plugins.md: - TTS row: 'Config-driven (not a Python plugin)', points at tts.md#custom-command-providers - STT row: points at tts.md#voice-message-transcription-stt (STT docs live in tts.md despite the filename) - Expanded note: TTS/STT use config-driven shell-command templates as their plugin surface (full tts.providers.<name> registry for TTS; HERMES_LOCAL_STT_COMMAND escape hatch for STT) - Any CLI that reads/writes files is automatically a plugin \u2014 no Python register_* API needed - Future register_tts_provider()/register_stt_provider() hooks mentioned as nice-to-have for SDK/streaming cases, not as the primary story build-a-hermes-plugin.md: - Same map update: TTS/STT rows explicit, footer note corrected Verified: - tts.md anchors (custom-command-providers, voice-message-transcription-stt) exist and resolve in docusaurus build (SUCCESS, no new broken links) * docs(plugins): expand pluggable interfaces table with MCP / event hooks / shell hooks / skill taps Broadened the scope beyond Python register_* hooks. Hermes has MULTIPLE plugin-style extension surfaces; they're now all in one table instead of being scattered across feature docs. Added rows for: - **MCP servers** — config.yaml mcp_servers.<name> auto-registers external tools from any MCP server. Huge extensibility surface, previously not linked from the plugin map. - **Gateway event hooks** — drop HOOK.yaml + handler.py into ~/.hermes/hooks/<name>/ to fire on gateway:startup, session:*, agent:*, command:* events. Separate from Python plugin hooks. - **Shell hooks** — hooks: block in config.yaml runs shell commands on events (notifications, auditing, etc.). - **Skill sources (taps)** — hermes skills tap add <repo> to pull in new skill registries beyond the built-in sources. Both docs updated: - user-guide/features/plugins.md: table column renamed to 'How' (mixes Python API + config-driven + drop-in-dir surfaces accurately) - guides/build-a-hermes-plugin.md: :::info map at top mirrors the new surfaces with a forward-link to the consolidated table Note block rewritten: instead of singling out TTS/STT as the 'different style' exception, now honestly describes that Hermes deliberately supports three plugin styles — Python APIs, config-driven commands, and drop-in manifest directories — and devs should pick the one that fits their integration. Not included (considered and rejected): - Transport layer (register_transport) — internal, not user-facing - Tool-call parsers — internal, VLLM phase-2 thing - Cloud browser providers — hardcoded registry, not drop-in yet - Terminal backends — hardcoded if/elif, not drop-in yet - Skill sources (the ABC) — hardcoded list, only taps are user-extensible Verified: - All 5 new anchors resolve (gateway-event-hooks, shell-hooks, skills-hub, custom-command-providers, voice-message-transcription-stt) - Docusaurus build SUCCESS, zero new broken links - Same 3 pre-existing broken links on main (cron-script-only, llms.txt, adding-platform-adapters#step-by-step-checklist) * docs(plugins): cover every pluggable surface in both the overview and how-to Both plugins.md and build-a-hermes-plugin.md now cover every extension surface end-to-end \u2014 general plugin APIs, specialized plugin types, config-driven surfaces \u2014 with concrete authoring patterns for each. plugins.md: - 'What plugins can do' table grows from 9 rows (general ctx.register_* only) to 14 rows covering register_platform, register_image_gen_provider, register_context_engine, MemoryProvider subclass, register_provider (model). Each row links to its full authoring guide. - New 'Plugin sub-categories' section under Plugin Discovery explains how plugins/platforms/, plugins/image_gen/, plugins/memory/, plugins/context_engine/, plugins/model-providers/ are routed to different loaders \u2014 PluginManager vs the per-category own-loader systems. - Explicit mention of user-override semantics at ~/.hermes/plugins/model-providers/ and ~/.hermes/plugins/memory/. build-a-hermes-plugin.md: - New '## Specialized plugin types' section (5 sub-sections): - Model provider plugins \u2014 ProviderProfile + plugin.yaml example, auto-wiring summary, link to full guide - Platform plugins \u2014 BasePlatformAdapter + register_platform() skeleton - Memory provider plugins \u2014 MemoryProvider subclass example - Context engine plugins \u2014 ContextEngine subclass example - Image-generation backends \u2014 ImageGenProvider + kind: backend example - New '## Non-Python extension surfaces' section (5 sub-sections): - MCP servers \u2014 config.yaml mcp_servers.<name> example - Gateway event hooks \u2014 HOOK.yaml + handler.py example - Shell hooks \u2014 hooks: block in config.yaml example - Skill sources (taps) \u2014 hermes skills tap add example - TTS / STT command templates \u2014 tts.providers.<name> with type: command - Distribute via pip / NixOS promoted from ### to ## (they were orphaned after the reorganization) Each specialized / non-Python section has a concrete, copy-pasteable example plus a 'Full guide:' link to the authoritative doc. Devs arriving at the build-a-hermes-plugin guide now see every extension surface at their disposal, not just the general tool/hook/slash-command surface. Verified: - Docusaurus build SUCCESS, zero new broken links - All new cross-links (developer-guide/model-provider-plugin, adding-platform-adapters, memory-provider-plugin, context-engine-plugin, user-guide/features/mcp, skills#skills-hub, hooks#gateway-event-hooks, hooks#shell-hooks, tts#custom-command-providers, tts#voice-message-transcription-stt) resolve - Same 3 pre-existing broken links on main (cron-script-only, llms.txt, adding-platform-adapters#step-by-step-checklist) * docs(plugins): fix opt-in inconsistency — not every plugin is gated The 'Every plugin is disabled by default' statement was wrong. Several plugin categories intentionally bypass plugins.enabled: - Bundled platform plugins (IRC, Teams) auto-load so shipped gateway channels are available out of the box. Activation per channel is via gateway.platforms.<name>.enabled. - Bundled backends (plugins/image_gen/*) auto-load so the default backend 'just works'. Selection via <category>.provider config. - Memory providers are all discovered; one is active via memory.provider. - Context engines are all discovered; one is active via context.engine. - Model providers: all 33 discovered at first get_provider_profile(); user picks via --provider / config. The plugins.enabled allow-list specifically gates: - Standalone plugins (general tools/hooks/slash commands) - User-installed backends - User-installed platforms (third-party gateway adapters) - Pip entry-point backends Which matches the actual code in hermes_cli/plugins.py:737 where the bundled+backend/platform check bypasses the allow-list. Rewrote '## Plugins are opt-in' to: - Retitle to 'Plugins are opt-in (with a few exceptions)' - Narrow opening claim to 'General plugins and user-installed backends are disabled by default' - Added 'What the allow-list does NOT gate' subsection with a full table of which bypass the gate and how they're activated instead - Fixed migration section wording (bundled platform/backend plugins never needed grandfathering) Verified: docusaurus build SUCCESS, zero new broken links.
2026-05-06 07:24:42 -07:00
### Plugin sub-categories
Within each source, Hermes also recognizes sub-category directories that route plugins to specialized discovery systems:
| Sub-directory | What it holds | Discovery system |
|---|---|---|
| `plugins/` (root) | General plugins — tools, hooks, slash commands, CLI commands, bundled skills | `PluginManager` (kind: `standalone` or `backend`) |
| `plugins/platforms/<name>/` | Gateway channel adapters (`ctx.register_platform()`) | `PluginManager` (kind: `platform`, one level deeper) |
| `plugins/image_gen/<name>/` | Image-generation backends (`ctx.register_image_gen_provider()`) | `PluginManager` (kind: `backend`, one level deeper) |
| `plugins/memory/<name>/` | Memory providers (subclass `MemoryProvider`) | **Own loader** in `plugins/memory/__init__.py` (kind: `exclusive` — one active at a time) |
| `plugins/context_engine/<name>/` | Context-compression engines (`ctx.register_context_engine()`) | **Own loader** in `plugins/context_engine/__init__.py` (one active at a time) |
| `plugins/model-providers/<name>/` | LLM provider profiles (`register_provider(ProviderProfile(...))`) | **Own loader** in `providers/__init__.py` (lazily scanned on first `get_provider_profile()` call) |
User plugins at `~/.hermes/plugins/model-providers/<name>/` and `~/.hermes/plugins/memory/<name>/` override bundled plugins of the same name — last-writer-wins in `register_provider()` / `register_memory_provider()`. Drop a directory in, and it replaces the built-in without any repo edits.
fix(plugins): surface category-namespaced plugins in hermes plugins list `_discover_all_plugins()` in plugins_cmd.py did a flat scan of the bundled and user plugin directories — only direct children with a plugin.yaml were surfaced. Category directories like `observability/`, `image_gen/`, `platforms/`, `model-providers/`, `web/`, and `video_gen/` have no plugin.yaml of their own, so their nested plugins (`observability/langfuse`, `image_gen/openai`, etc.) never appeared in `hermes plugins list` or the interactive `hermes plugins` UI — even though the runtime loader (`PluginManager._scan_directory_level`) discovers them correctly and they do load at runtime. This broke the documented promise that bundled plugins appear in `hermes plugins list` and the interactive UI before being enabled, and made it look like `observability/langfuse` didn't exist. Refactor `_discover_all_plugins()` to mirror the loader's recursion (depth cap = 2, same skip set, user overrides bundled on key collision). Return the path-derived registry key (e.g. `observability/langfuse`) as the displayed name, matching what the user passes to `hermes plugins enable …` / writes under `plugins.enabled` in config.yaml. Also clarify the plugins docs: spell out that sub-category plugins surface by their `<category>/<plugin>` key in `hermes plugins list` / interactive UI, add an `observability/langfuse` example to the command reference, and include a nested entry in the interactive-UI mock. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-16 22:55:28 +00:00
Sub-category plugins surface in `hermes plugins list` and the interactive `hermes plugins` UI under their **path-derived key** — e.g. `observability/langfuse`, `image_gen/openai`, `platforms/teams`. That key (not the bare manifest `name:`) is the value you pass to `hermes plugins enable …` / `disable …` and the string to add under `plugins.enabled` in `config.yaml`.
docs: pluggable surfaces coverage — model-provider guide, full plugin map, opt-in fix (#20749) * docs(providers): add model-provider-plugin authoring guide + fix stale refs New docs: - website/docs/developer-guide/model-provider-plugin.md — full authoring guide (directory layout, minimal example, ProviderProfile fields, overridable hooks, user overrides, api_mode selection, auth types, testing, pip distribution) - Wired into website/sidebars.ts under 'Extending' - Cross-references added in: - guides/build-a-hermes-plugin.md (tip block) - developer-guide/adding-providers.md - developer-guide/provider-runtime.md User guide: - user-guide/features/plugins.md: Plugin types table grows from 3 to 4 with 'Model providers' row Stale comment cleanup (providers/*.py → plugins/model-providers/<name>/): - hermes_cli/main.py:_is_profile_api_key_provider docstring - hermes_cli/doctor.py:_build_apikey_providers_list docstring - hermes_cli/auth.py: PROVIDER_REGISTRY + alias auto-extension comments - hermes_cli/models.py: CANONICAL_PROVIDERS auto-extension comment AGENTS.md: - Project-structure tree: added plugins/model-providers/ row - New section: 'Model-provider plugins' explaining discovery, override semantics, PluginManager integration, kind auto-coerce heuristic Verified: docusaurus build succeeds, new page renders, all 3 cross-links resolve. 347/347 targeted tests pass (tests/providers/, tests/hermes_cli/test_plugins.py, tests/hermes_cli/test_runtime_provider_resolution.py, tests/run_agent/test_provider_parity.py). * docs(plugins): add 'pluggable interfaces at a glance' maps to plugins.md + build-a-hermes-plugin Devs landing on either the user-guide plugin page or the build-a-plugin guide now get an upfront table of every distinct pluggable surface with a link to the right authoring doc. Previously they'd have to read the full general-plugin guide to discover that model providers / platforms / memory / context engines are separate systems. user-guide/features/plugins.md: - New 'Pluggable interfaces — where to go for each' section below the existing 4-kinds table - 10 rows covering every register_* surface (tool, hook, slash command, CLI subcommand, skill, model provider, platform, memory, context engine, image-gen) - Explicit note: TTS/STT are NOT plugin-extensible yet — documented with a pointer to the current config.yaml 'command providers' pattern and a note that register_tts_provider()/register_stt_provider() may come later guides/build-a-hermes-plugin.md: - New :::info 'Not sure which guide you need?' map at the top so devs see all pluggable interfaces before investing in this 737-line general-plugin walkthrough - Existing bottom :::tip expanded to include platform adapters alongside model/memory/context plugins Verified: - All 8 cross-doc links in the new plugins.md table resolve in a docusaurus build (SUCCESS, no new broken links) - TTS link corrected (features/voice → features/tts; latter exists) - Pre-existing broken links/anchors (cron-script-only, llms.txt, adding-platform-adapters#step-by-step-checklist) are unchanged * docs(plugins): correct TTS/STT pluggability \u2014 they ARE plugins (command-providers) Previous commit incorrectly said TTS/STT 'aren't plugin-extensible'. They are, via the config-driven command-provider pattern \u2014 any CLI that reads text and writes audio (or vice versa for STT) is automatically a plugin with zero Python. The tts.md docs cover this extensively and I missed it. plugins.md: - TTS row: 'Config-driven (not a Python plugin)', points at tts.md#custom-command-providers - STT row: points at tts.md#voice-message-transcription-stt (STT docs live in tts.md despite the filename) - Expanded note: TTS/STT use config-driven shell-command templates as their plugin surface (full tts.providers.<name> registry for TTS; HERMES_LOCAL_STT_COMMAND escape hatch for STT) - Any CLI that reads/writes files is automatically a plugin \u2014 no Python register_* API needed - Future register_tts_provider()/register_stt_provider() hooks mentioned as nice-to-have for SDK/streaming cases, not as the primary story build-a-hermes-plugin.md: - Same map update: TTS/STT rows explicit, footer note corrected Verified: - tts.md anchors (custom-command-providers, voice-message-transcription-stt) exist and resolve in docusaurus build (SUCCESS, no new broken links) * docs(plugins): expand pluggable interfaces table with MCP / event hooks / shell hooks / skill taps Broadened the scope beyond Python register_* hooks. Hermes has MULTIPLE plugin-style extension surfaces; they're now all in one table instead of being scattered across feature docs. Added rows for: - **MCP servers** — config.yaml mcp_servers.<name> auto-registers external tools from any MCP server. Huge extensibility surface, previously not linked from the plugin map. - **Gateway event hooks** — drop HOOK.yaml + handler.py into ~/.hermes/hooks/<name>/ to fire on gateway:startup, session:*, agent:*, command:* events. Separate from Python plugin hooks. - **Shell hooks** — hooks: block in config.yaml runs shell commands on events (notifications, auditing, etc.). - **Skill sources (taps)** — hermes skills tap add <repo> to pull in new skill registries beyond the built-in sources. Both docs updated: - user-guide/features/plugins.md: table column renamed to 'How' (mixes Python API + config-driven + drop-in-dir surfaces accurately) - guides/build-a-hermes-plugin.md: :::info map at top mirrors the new surfaces with a forward-link to the consolidated table Note block rewritten: instead of singling out TTS/STT as the 'different style' exception, now honestly describes that Hermes deliberately supports three plugin styles — Python APIs, config-driven commands, and drop-in manifest directories — and devs should pick the one that fits their integration. Not included (considered and rejected): - Transport layer (register_transport) — internal, not user-facing - Tool-call parsers — internal, VLLM phase-2 thing - Cloud browser providers — hardcoded registry, not drop-in yet - Terminal backends — hardcoded if/elif, not drop-in yet - Skill sources (the ABC) — hardcoded list, only taps are user-extensible Verified: - All 5 new anchors resolve (gateway-event-hooks, shell-hooks, skills-hub, custom-command-providers, voice-message-transcription-stt) - Docusaurus build SUCCESS, zero new broken links - Same 3 pre-existing broken links on main (cron-script-only, llms.txt, adding-platform-adapters#step-by-step-checklist) * docs(plugins): cover every pluggable surface in both the overview and how-to Both plugins.md and build-a-hermes-plugin.md now cover every extension surface end-to-end \u2014 general plugin APIs, specialized plugin types, config-driven surfaces \u2014 with concrete authoring patterns for each. plugins.md: - 'What plugins can do' table grows from 9 rows (general ctx.register_* only) to 14 rows covering register_platform, register_image_gen_provider, register_context_engine, MemoryProvider subclass, register_provider (model). Each row links to its full authoring guide. - New 'Plugin sub-categories' section under Plugin Discovery explains how plugins/platforms/, plugins/image_gen/, plugins/memory/, plugins/context_engine/, plugins/model-providers/ are routed to different loaders \u2014 PluginManager vs the per-category own-loader systems. - Explicit mention of user-override semantics at ~/.hermes/plugins/model-providers/ and ~/.hermes/plugins/memory/. build-a-hermes-plugin.md: - New '## Specialized plugin types' section (5 sub-sections): - Model provider plugins \u2014 ProviderProfile + plugin.yaml example, auto-wiring summary, link to full guide - Platform plugins \u2014 BasePlatformAdapter + register_platform() skeleton - Memory provider plugins \u2014 MemoryProvider subclass example - Context engine plugins \u2014 ContextEngine subclass example - Image-generation backends \u2014 ImageGenProvider + kind: backend example - New '## Non-Python extension surfaces' section (5 sub-sections): - MCP servers \u2014 config.yaml mcp_servers.<name> example - Gateway event hooks \u2014 HOOK.yaml + handler.py example - Shell hooks \u2014 hooks: block in config.yaml example - Skill sources (taps) \u2014 hermes skills tap add example - TTS / STT command templates \u2014 tts.providers.<name> with type: command - Distribute via pip / NixOS promoted from ### to ## (they were orphaned after the reorganization) Each specialized / non-Python section has a concrete, copy-pasteable example plus a 'Full guide:' link to the authoritative doc. Devs arriving at the build-a-hermes-plugin guide now see every extension surface at their disposal, not just the general tool/hook/slash-command surface. Verified: - Docusaurus build SUCCESS, zero new broken links - All new cross-links (developer-guide/model-provider-plugin, adding-platform-adapters, memory-provider-plugin, context-engine-plugin, user-guide/features/mcp, skills#skills-hub, hooks#gateway-event-hooks, hooks#shell-hooks, tts#custom-command-providers, tts#voice-message-transcription-stt) resolve - Same 3 pre-existing broken links on main (cron-script-only, llms.txt, adding-platform-adapters#step-by-step-checklist) * docs(plugins): fix opt-in inconsistency — not every plugin is gated The 'Every plugin is disabled by default' statement was wrong. Several plugin categories intentionally bypass plugins.enabled: - Bundled platform plugins (IRC, Teams) auto-load so shipped gateway channels are available out of the box. Activation per channel is via gateway.platforms.<name>.enabled. - Bundled backends (plugins/image_gen/*) auto-load so the default backend 'just works'. Selection via <category>.provider config. - Memory providers are all discovered; one is active via memory.provider. - Context engines are all discovered; one is active via context.engine. - Model providers: all 33 discovered at first get_provider_profile(); user picks via --provider / config. The plugins.enabled allow-list specifically gates: - Standalone plugins (general tools/hooks/slash commands) - User-installed backends - User-installed platforms (third-party gateway adapters) - Pip entry-point backends Which matches the actual code in hermes_cli/plugins.py:737 where the bundled+backend/platform check bypasses the allow-list. Rewrote '## Plugins are opt-in' to: - Retitle to 'Plugins are opt-in (with a few exceptions)' - Narrow opening claim to 'General plugins and user-installed backends are disabled by default' - Added 'What the allow-list does NOT gate' subsection with a full table of which bypass the gate and how they're activated instead - Fixed migration section wording (bundled platform/backend plugins never needed grandfathering) Verified: docusaurus build SUCCESS, zero new broken links.
2026-05-06 07:24:42 -07:00
## Plugins are opt-in (with a few exceptions)
feat(plugins): make all plugins opt-in by default Plugins now require explicit consent to load. Discovery still finds every plugin — user-installed, bundled, and pip — so they all show up in `hermes plugins` and `/plugins`, but the loader only instantiates plugins whose name appears in `plugins.enabled` in config.yaml. This removes the previous ambient-execution risk where a newly-installed or bundled plugin could register hooks, tools, and commands on first run without the user opting in. The three-state model is now explicit: enabled — in plugins.enabled, loads on next session disabled — in plugins.disabled, never loads (wins over enabled) not enabled — discovered but never opted in (default for new installs) `hermes plugins install <repo>` prompts "Enable 'name' now? [y/N]" (defaults to no). New `--enable` / `--no-enable` flags skip the prompt for scripted installs. `hermes plugins enable/disable` manage both lists so a disabled plugin stays explicitly off even if something later adds it to enabled. Config migration (schema v20 → v21): existing user plugins already installed under ~/.hermes/plugins/ (minus anything in plugins.disabled) are auto-grandfathered into plugins.enabled so upgrades don't silently break working setups. Bundled plugins are NOT grandfathered — even existing users have to opt in explicitly. Also: HERMES_DISABLE_BUNDLED_PLUGINS env var removed (redundant with opt-in default), cmd_list now shows bundled + user plugins together with their three-state status, interactive UI tags bundled entries [bundled], docs updated across plugins.md and built-in-plugins.md. Validation: 442 plugin/config tests pass. E2E: fresh install discovers disk-cleanup but does not load it; `hermes plugins enable disk-cleanup` activates hooks; migration grandfathers existing user plugins correctly while leaving bundled plugins off.
2026-04-20 04:40:17 -07:00
docs: pluggable surfaces coverage — model-provider guide, full plugin map, opt-in fix (#20749) * docs(providers): add model-provider-plugin authoring guide + fix stale refs New docs: - website/docs/developer-guide/model-provider-plugin.md — full authoring guide (directory layout, minimal example, ProviderProfile fields, overridable hooks, user overrides, api_mode selection, auth types, testing, pip distribution) - Wired into website/sidebars.ts under 'Extending' - Cross-references added in: - guides/build-a-hermes-plugin.md (tip block) - developer-guide/adding-providers.md - developer-guide/provider-runtime.md User guide: - user-guide/features/plugins.md: Plugin types table grows from 3 to 4 with 'Model providers' row Stale comment cleanup (providers/*.py → plugins/model-providers/<name>/): - hermes_cli/main.py:_is_profile_api_key_provider docstring - hermes_cli/doctor.py:_build_apikey_providers_list docstring - hermes_cli/auth.py: PROVIDER_REGISTRY + alias auto-extension comments - hermes_cli/models.py: CANONICAL_PROVIDERS auto-extension comment AGENTS.md: - Project-structure tree: added plugins/model-providers/ row - New section: 'Model-provider plugins' explaining discovery, override semantics, PluginManager integration, kind auto-coerce heuristic Verified: docusaurus build succeeds, new page renders, all 3 cross-links resolve. 347/347 targeted tests pass (tests/providers/, tests/hermes_cli/test_plugins.py, tests/hermes_cli/test_runtime_provider_resolution.py, tests/run_agent/test_provider_parity.py). * docs(plugins): add 'pluggable interfaces at a glance' maps to plugins.md + build-a-hermes-plugin Devs landing on either the user-guide plugin page or the build-a-plugin guide now get an upfront table of every distinct pluggable surface with a link to the right authoring doc. Previously they'd have to read the full general-plugin guide to discover that model providers / platforms / memory / context engines are separate systems. user-guide/features/plugins.md: - New 'Pluggable interfaces — where to go for each' section below the existing 4-kinds table - 10 rows covering every register_* surface (tool, hook, slash command, CLI subcommand, skill, model provider, platform, memory, context engine, image-gen) - Explicit note: TTS/STT are NOT plugin-extensible yet — documented with a pointer to the current config.yaml 'command providers' pattern and a note that register_tts_provider()/register_stt_provider() may come later guides/build-a-hermes-plugin.md: - New :::info 'Not sure which guide you need?' map at the top so devs see all pluggable interfaces before investing in this 737-line general-plugin walkthrough - Existing bottom :::tip expanded to include platform adapters alongside model/memory/context plugins Verified: - All 8 cross-doc links in the new plugins.md table resolve in a docusaurus build (SUCCESS, no new broken links) - TTS link corrected (features/voice → features/tts; latter exists) - Pre-existing broken links/anchors (cron-script-only, llms.txt, adding-platform-adapters#step-by-step-checklist) are unchanged * docs(plugins): correct TTS/STT pluggability \u2014 they ARE plugins (command-providers) Previous commit incorrectly said TTS/STT 'aren't plugin-extensible'. They are, via the config-driven command-provider pattern \u2014 any CLI that reads text and writes audio (or vice versa for STT) is automatically a plugin with zero Python. The tts.md docs cover this extensively and I missed it. plugins.md: - TTS row: 'Config-driven (not a Python plugin)', points at tts.md#custom-command-providers - STT row: points at tts.md#voice-message-transcription-stt (STT docs live in tts.md despite the filename) - Expanded note: TTS/STT use config-driven shell-command templates as their plugin surface (full tts.providers.<name> registry for TTS; HERMES_LOCAL_STT_COMMAND escape hatch for STT) - Any CLI that reads/writes files is automatically a plugin \u2014 no Python register_* API needed - Future register_tts_provider()/register_stt_provider() hooks mentioned as nice-to-have for SDK/streaming cases, not as the primary story build-a-hermes-plugin.md: - Same map update: TTS/STT rows explicit, footer note corrected Verified: - tts.md anchors (custom-command-providers, voice-message-transcription-stt) exist and resolve in docusaurus build (SUCCESS, no new broken links) * docs(plugins): expand pluggable interfaces table with MCP / event hooks / shell hooks / skill taps Broadened the scope beyond Python register_* hooks. Hermes has MULTIPLE plugin-style extension surfaces; they're now all in one table instead of being scattered across feature docs. Added rows for: - **MCP servers** — config.yaml mcp_servers.<name> auto-registers external tools from any MCP server. Huge extensibility surface, previously not linked from the plugin map. - **Gateway event hooks** — drop HOOK.yaml + handler.py into ~/.hermes/hooks/<name>/ to fire on gateway:startup, session:*, agent:*, command:* events. Separate from Python plugin hooks. - **Shell hooks** — hooks: block in config.yaml runs shell commands on events (notifications, auditing, etc.). - **Skill sources (taps)** — hermes skills tap add <repo> to pull in new skill registries beyond the built-in sources. Both docs updated: - user-guide/features/plugins.md: table column renamed to 'How' (mixes Python API + config-driven + drop-in-dir surfaces accurately) - guides/build-a-hermes-plugin.md: :::info map at top mirrors the new surfaces with a forward-link to the consolidated table Note block rewritten: instead of singling out TTS/STT as the 'different style' exception, now honestly describes that Hermes deliberately supports three plugin styles — Python APIs, config-driven commands, and drop-in manifest directories — and devs should pick the one that fits their integration. Not included (considered and rejected): - Transport layer (register_transport) — internal, not user-facing - Tool-call parsers — internal, VLLM phase-2 thing - Cloud browser providers — hardcoded registry, not drop-in yet - Terminal backends — hardcoded if/elif, not drop-in yet - Skill sources (the ABC) — hardcoded list, only taps are user-extensible Verified: - All 5 new anchors resolve (gateway-event-hooks, shell-hooks, skills-hub, custom-command-providers, voice-message-transcription-stt) - Docusaurus build SUCCESS, zero new broken links - Same 3 pre-existing broken links on main (cron-script-only, llms.txt, adding-platform-adapters#step-by-step-checklist) * docs(plugins): cover every pluggable surface in both the overview and how-to Both plugins.md and build-a-hermes-plugin.md now cover every extension surface end-to-end \u2014 general plugin APIs, specialized plugin types, config-driven surfaces \u2014 with concrete authoring patterns for each. plugins.md: - 'What plugins can do' table grows from 9 rows (general ctx.register_* only) to 14 rows covering register_platform, register_image_gen_provider, register_context_engine, MemoryProvider subclass, register_provider (model). Each row links to its full authoring guide. - New 'Plugin sub-categories' section under Plugin Discovery explains how plugins/platforms/, plugins/image_gen/, plugins/memory/, plugins/context_engine/, plugins/model-providers/ are routed to different loaders \u2014 PluginManager vs the per-category own-loader systems. - Explicit mention of user-override semantics at ~/.hermes/plugins/model-providers/ and ~/.hermes/plugins/memory/. build-a-hermes-plugin.md: - New '## Specialized plugin types' section (5 sub-sections): - Model provider plugins \u2014 ProviderProfile + plugin.yaml example, auto-wiring summary, link to full guide - Platform plugins \u2014 BasePlatformAdapter + register_platform() skeleton - Memory provider plugins \u2014 MemoryProvider subclass example - Context engine plugins \u2014 ContextEngine subclass example - Image-generation backends \u2014 ImageGenProvider + kind: backend example - New '## Non-Python extension surfaces' section (5 sub-sections): - MCP servers \u2014 config.yaml mcp_servers.<name> example - Gateway event hooks \u2014 HOOK.yaml + handler.py example - Shell hooks \u2014 hooks: block in config.yaml example - Skill sources (taps) \u2014 hermes skills tap add example - TTS / STT command templates \u2014 tts.providers.<name> with type: command - Distribute via pip / NixOS promoted from ### to ## (they were orphaned after the reorganization) Each specialized / non-Python section has a concrete, copy-pasteable example plus a 'Full guide:' link to the authoritative doc. Devs arriving at the build-a-hermes-plugin guide now see every extension surface at their disposal, not just the general tool/hook/slash-command surface. Verified: - Docusaurus build SUCCESS, zero new broken links - All new cross-links (developer-guide/model-provider-plugin, adding-platform-adapters, memory-provider-plugin, context-engine-plugin, user-guide/features/mcp, skills#skills-hub, hooks#gateway-event-hooks, hooks#shell-hooks, tts#custom-command-providers, tts#voice-message-transcription-stt) resolve - Same 3 pre-existing broken links on main (cron-script-only, llms.txt, adding-platform-adapters#step-by-step-checklist) * docs(plugins): fix opt-in inconsistency — not every plugin is gated The 'Every plugin is disabled by default' statement was wrong. Several plugin categories intentionally bypass plugins.enabled: - Bundled platform plugins (IRC, Teams) auto-load so shipped gateway channels are available out of the box. Activation per channel is via gateway.platforms.<name>.enabled. - Bundled backends (plugins/image_gen/*) auto-load so the default backend 'just works'. Selection via <category>.provider config. - Memory providers are all discovered; one is active via memory.provider. - Context engines are all discovered; one is active via context.engine. - Model providers: all 33 discovered at first get_provider_profile(); user picks via --provider / config. The plugins.enabled allow-list specifically gates: - Standalone plugins (general tools/hooks/slash commands) - User-installed backends - User-installed platforms (third-party gateway adapters) - Pip entry-point backends Which matches the actual code in hermes_cli/plugins.py:737 where the bundled+backend/platform check bypasses the allow-list. Rewrote '## Plugins are opt-in' to: - Retitle to 'Plugins are opt-in (with a few exceptions)' - Narrow opening claim to 'General plugins and user-installed backends are disabled by default' - Added 'What the allow-list does NOT gate' subsection with a full table of which bypass the gate and how they're activated instead - Fixed migration section wording (bundled platform/backend plugins never needed grandfathering) Verified: docusaurus build SUCCESS, zero new broken links.
2026-05-06 07:24:42 -07:00
**General plugins and user-installed backends are disabled by default** — discovery finds them (so they show up in `hermes plugins` and `/plugins`), but nothing with hooks or tools loads until you add the plugin's name to `plugins.enabled` in `~/.hermes/config.yaml`. This stops third-party code from running without your explicit consent.
feat(plugins): make all plugins opt-in by default Plugins now require explicit consent to load. Discovery still finds every plugin — user-installed, bundled, and pip — so they all show up in `hermes plugins` and `/plugins`, but the loader only instantiates plugins whose name appears in `plugins.enabled` in config.yaml. This removes the previous ambient-execution risk where a newly-installed or bundled plugin could register hooks, tools, and commands on first run without the user opting in. The three-state model is now explicit: enabled — in plugins.enabled, loads on next session disabled — in plugins.disabled, never loads (wins over enabled) not enabled — discovered but never opted in (default for new installs) `hermes plugins install <repo>` prompts "Enable 'name' now? [y/N]" (defaults to no). New `--enable` / `--no-enable` flags skip the prompt for scripted installs. `hermes plugins enable/disable` manage both lists so a disabled plugin stays explicitly off even if something later adds it to enabled. Config migration (schema v20 → v21): existing user plugins already installed under ~/.hermes/plugins/ (minus anything in plugins.disabled) are auto-grandfathered into plugins.enabled so upgrades don't silently break working setups. Bundled plugins are NOT grandfathered — even existing users have to opt in explicitly. Also: HERMES_DISABLE_BUNDLED_PLUGINS env var removed (redundant with opt-in default), cmd_list now shows bundled + user plugins together with their three-state status, interactive UI tags bundled entries [bundled], docs updated across plugins.md and built-in-plugins.md. Validation: 442 plugin/config tests pass. E2E: fresh install discovers disk-cleanup but does not load it; `hermes plugins enable disk-cleanup` activates hooks; migration grandfathers existing user plugins correctly while leaving bundled plugins off.
2026-04-20 04:40:17 -07:00
```yaml
plugins:
enabled:
- my-tool-plugin
- disk-cleanup
disabled: # optional deny-list — always wins if a name appears in both
- noisy-plugin
```
Three ways to flip state:
```bash
hermes plugins # interactive toggle (space to check/uncheck)
hermes plugins enable <name> # add to allow-list
hermes plugins disable <name> # remove from allow-list + add to disabled
```
After `hermes plugins install owner/repo`, you're asked `Enable 'name' now? [y/N]` — defaults to no. Skip the prompt for scripted installs with `--enable` or `--no-enable`.
docs: pluggable surfaces coverage — model-provider guide, full plugin map, opt-in fix (#20749) * docs(providers): add model-provider-plugin authoring guide + fix stale refs New docs: - website/docs/developer-guide/model-provider-plugin.md — full authoring guide (directory layout, minimal example, ProviderProfile fields, overridable hooks, user overrides, api_mode selection, auth types, testing, pip distribution) - Wired into website/sidebars.ts under 'Extending' - Cross-references added in: - guides/build-a-hermes-plugin.md (tip block) - developer-guide/adding-providers.md - developer-guide/provider-runtime.md User guide: - user-guide/features/plugins.md: Plugin types table grows from 3 to 4 with 'Model providers' row Stale comment cleanup (providers/*.py → plugins/model-providers/<name>/): - hermes_cli/main.py:_is_profile_api_key_provider docstring - hermes_cli/doctor.py:_build_apikey_providers_list docstring - hermes_cli/auth.py: PROVIDER_REGISTRY + alias auto-extension comments - hermes_cli/models.py: CANONICAL_PROVIDERS auto-extension comment AGENTS.md: - Project-structure tree: added plugins/model-providers/ row - New section: 'Model-provider plugins' explaining discovery, override semantics, PluginManager integration, kind auto-coerce heuristic Verified: docusaurus build succeeds, new page renders, all 3 cross-links resolve. 347/347 targeted tests pass (tests/providers/, tests/hermes_cli/test_plugins.py, tests/hermes_cli/test_runtime_provider_resolution.py, tests/run_agent/test_provider_parity.py). * docs(plugins): add 'pluggable interfaces at a glance' maps to plugins.md + build-a-hermes-plugin Devs landing on either the user-guide plugin page or the build-a-plugin guide now get an upfront table of every distinct pluggable surface with a link to the right authoring doc. Previously they'd have to read the full general-plugin guide to discover that model providers / platforms / memory / context engines are separate systems. user-guide/features/plugins.md: - New 'Pluggable interfaces — where to go for each' section below the existing 4-kinds table - 10 rows covering every register_* surface (tool, hook, slash command, CLI subcommand, skill, model provider, platform, memory, context engine, image-gen) - Explicit note: TTS/STT are NOT plugin-extensible yet — documented with a pointer to the current config.yaml 'command providers' pattern and a note that register_tts_provider()/register_stt_provider() may come later guides/build-a-hermes-plugin.md: - New :::info 'Not sure which guide you need?' map at the top so devs see all pluggable interfaces before investing in this 737-line general-plugin walkthrough - Existing bottom :::tip expanded to include platform adapters alongside model/memory/context plugins Verified: - All 8 cross-doc links in the new plugins.md table resolve in a docusaurus build (SUCCESS, no new broken links) - TTS link corrected (features/voice → features/tts; latter exists) - Pre-existing broken links/anchors (cron-script-only, llms.txt, adding-platform-adapters#step-by-step-checklist) are unchanged * docs(plugins): correct TTS/STT pluggability \u2014 they ARE plugins (command-providers) Previous commit incorrectly said TTS/STT 'aren't plugin-extensible'. They are, via the config-driven command-provider pattern \u2014 any CLI that reads text and writes audio (or vice versa for STT) is automatically a plugin with zero Python. The tts.md docs cover this extensively and I missed it. plugins.md: - TTS row: 'Config-driven (not a Python plugin)', points at tts.md#custom-command-providers - STT row: points at tts.md#voice-message-transcription-stt (STT docs live in tts.md despite the filename) - Expanded note: TTS/STT use config-driven shell-command templates as their plugin surface (full tts.providers.<name> registry for TTS; HERMES_LOCAL_STT_COMMAND escape hatch for STT) - Any CLI that reads/writes files is automatically a plugin \u2014 no Python register_* API needed - Future register_tts_provider()/register_stt_provider() hooks mentioned as nice-to-have for SDK/streaming cases, not as the primary story build-a-hermes-plugin.md: - Same map update: TTS/STT rows explicit, footer note corrected Verified: - tts.md anchors (custom-command-providers, voice-message-transcription-stt) exist and resolve in docusaurus build (SUCCESS, no new broken links) * docs(plugins): expand pluggable interfaces table with MCP / event hooks / shell hooks / skill taps Broadened the scope beyond Python register_* hooks. Hermes has MULTIPLE plugin-style extension surfaces; they're now all in one table instead of being scattered across feature docs. Added rows for: - **MCP servers** — config.yaml mcp_servers.<name> auto-registers external tools from any MCP server. Huge extensibility surface, previously not linked from the plugin map. - **Gateway event hooks** — drop HOOK.yaml + handler.py into ~/.hermes/hooks/<name>/ to fire on gateway:startup, session:*, agent:*, command:* events. Separate from Python plugin hooks. - **Shell hooks** — hooks: block in config.yaml runs shell commands on events (notifications, auditing, etc.). - **Skill sources (taps)** — hermes skills tap add <repo> to pull in new skill registries beyond the built-in sources. Both docs updated: - user-guide/features/plugins.md: table column renamed to 'How' (mixes Python API + config-driven + drop-in-dir surfaces accurately) - guides/build-a-hermes-plugin.md: :::info map at top mirrors the new surfaces with a forward-link to the consolidated table Note block rewritten: instead of singling out TTS/STT as the 'different style' exception, now honestly describes that Hermes deliberately supports three plugin styles — Python APIs, config-driven commands, and drop-in manifest directories — and devs should pick the one that fits their integration. Not included (considered and rejected): - Transport layer (register_transport) — internal, not user-facing - Tool-call parsers — internal, VLLM phase-2 thing - Cloud browser providers — hardcoded registry, not drop-in yet - Terminal backends — hardcoded if/elif, not drop-in yet - Skill sources (the ABC) — hardcoded list, only taps are user-extensible Verified: - All 5 new anchors resolve (gateway-event-hooks, shell-hooks, skills-hub, custom-command-providers, voice-message-transcription-stt) - Docusaurus build SUCCESS, zero new broken links - Same 3 pre-existing broken links on main (cron-script-only, llms.txt, adding-platform-adapters#step-by-step-checklist) * docs(plugins): cover every pluggable surface in both the overview and how-to Both plugins.md and build-a-hermes-plugin.md now cover every extension surface end-to-end \u2014 general plugin APIs, specialized plugin types, config-driven surfaces \u2014 with concrete authoring patterns for each. plugins.md: - 'What plugins can do' table grows from 9 rows (general ctx.register_* only) to 14 rows covering register_platform, register_image_gen_provider, register_context_engine, MemoryProvider subclass, register_provider (model). Each row links to its full authoring guide. - New 'Plugin sub-categories' section under Plugin Discovery explains how plugins/platforms/, plugins/image_gen/, plugins/memory/, plugins/context_engine/, plugins/model-providers/ are routed to different loaders \u2014 PluginManager vs the per-category own-loader systems. - Explicit mention of user-override semantics at ~/.hermes/plugins/model-providers/ and ~/.hermes/plugins/memory/. build-a-hermes-plugin.md: - New '## Specialized plugin types' section (5 sub-sections): - Model provider plugins \u2014 ProviderProfile + plugin.yaml example, auto-wiring summary, link to full guide - Platform plugins \u2014 BasePlatformAdapter + register_platform() skeleton - Memory provider plugins \u2014 MemoryProvider subclass example - Context engine plugins \u2014 ContextEngine subclass example - Image-generation backends \u2014 ImageGenProvider + kind: backend example - New '## Non-Python extension surfaces' section (5 sub-sections): - MCP servers \u2014 config.yaml mcp_servers.<name> example - Gateway event hooks \u2014 HOOK.yaml + handler.py example - Shell hooks \u2014 hooks: block in config.yaml example - Skill sources (taps) \u2014 hermes skills tap add example - TTS / STT command templates \u2014 tts.providers.<name> with type: command - Distribute via pip / NixOS promoted from ### to ## (they were orphaned after the reorganization) Each specialized / non-Python section has a concrete, copy-pasteable example plus a 'Full guide:' link to the authoritative doc. Devs arriving at the build-a-hermes-plugin guide now see every extension surface at their disposal, not just the general tool/hook/slash-command surface. Verified: - Docusaurus build SUCCESS, zero new broken links - All new cross-links (developer-guide/model-provider-plugin, adding-platform-adapters, memory-provider-plugin, context-engine-plugin, user-guide/features/mcp, skills#skills-hub, hooks#gateway-event-hooks, hooks#shell-hooks, tts#custom-command-providers, tts#voice-message-transcription-stt) resolve - Same 3 pre-existing broken links on main (cron-script-only, llms.txt, adding-platform-adapters#step-by-step-checklist) * docs(plugins): fix opt-in inconsistency — not every plugin is gated The 'Every plugin is disabled by default' statement was wrong. Several plugin categories intentionally bypass plugins.enabled: - Bundled platform plugins (IRC, Teams) auto-load so shipped gateway channels are available out of the box. Activation per channel is via gateway.platforms.<name>.enabled. - Bundled backends (plugins/image_gen/*) auto-load so the default backend 'just works'. Selection via <category>.provider config. - Memory providers are all discovered; one is active via memory.provider. - Context engines are all discovered; one is active via context.engine. - Model providers: all 33 discovered at first get_provider_profile(); user picks via --provider / config. The plugins.enabled allow-list specifically gates: - Standalone plugins (general tools/hooks/slash commands) - User-installed backends - User-installed platforms (third-party gateway adapters) - Pip entry-point backends Which matches the actual code in hermes_cli/plugins.py:737 where the bundled+backend/platform check bypasses the allow-list. Rewrote '## Plugins are opt-in' to: - Retitle to 'Plugins are opt-in (with a few exceptions)' - Narrow opening claim to 'General plugins and user-installed backends are disabled by default' - Added 'What the allow-list does NOT gate' subsection with a full table of which bypass the gate and how they're activated instead - Fixed migration section wording (bundled platform/backend plugins never needed grandfathering) Verified: docusaurus build SUCCESS, zero new broken links.
2026-05-06 07:24:42 -07:00
### What the allow-list does NOT gate
Several categories of plugin bypass `plugins.enabled` — they're part of Hermes' built-in surface and would break basic functionality if gated off by default:
| Plugin kind | How it's activated instead |
|---|---|
| **Bundled platform plugins** (IRC, Teams, etc. under `plugins/platforms/`) | Auto-loaded so every shipped gateway channel is available. The actual channel turns on via `gateway.platforms.<name>.enabled` in `config.yaml`. |
| **Bundled backends** (image-gen providers under `plugins/image_gen/`, etc.) | Auto-loaded so the default backend "just works". Selection happens via `<category>.provider` in `config.yaml` (e.g. `image_gen.provider: openai`). |
| **Memory providers** (`plugins/memory/`) | All discovered; exactly one is active, chosen by `memory.provider` in `config.yaml`. |
| **Context engines** (`plugins/context_engine/`) | All discovered; one is active, chosen by `context.engine` in `config.yaml`. |
docs: deep audit — fix stale config keys, missing commands, and registry drift (#22784) * docs: deep audit — fix stale config keys, missing commands, and registry drift Cross-checked ~80 high-impact docs pages (getting-started, reference, top-level user-guide, user-guide/features) against the live registries: hermes_cli/commands.py COMMAND_REGISTRY (slash commands) hermes_cli/auth.py PROVIDER_REGISTRY (providers) hermes_cli/config.py DEFAULT_CONFIG (config keys) toolsets.py TOOLSETS (toolsets) tools/registry.py get_all_tool_names() (tools) python -m hermes_cli.main <subcmd> --help (CLI args) reference/ - cli-commands.md: drop duplicate hermes fallback row + duplicate section, add stepfun/lmstudio to --provider enum, expand auth/mcp/curator subcommand lists to match --help output (status/logout/spotify, login, archive/prune/ list-archived). - slash-commands.md: add missing /sessions and /reload-skills entries + correct the cross-platform Notes line. - tools-reference.md: drop bogus '68 tools' headline, drop fictional 'browser-cdp toolset' (these tools live in 'browser' and are runtime-gated), add missing 'kanban' and 'video' toolset sections, fix MCP example to use the real mcp_<server>_<tool> prefix. - toolsets-reference.md: list browser_cdp/browser_dialog inside the 'browser' row, add missing 'kanban' and 'video' toolset rows, drop the stale '38 tools' count for hermes-cli. - profile-commands.md: add missing install/update/info subcommands, document fish completion. - environment-variables.md: dedupe GMI_API_KEY/GMI_BASE_URL rows (kept the one with the correct gmi-serving.com default). - faq.md: Anthropic/Google/OpenAI examples — direct providers exist (not just via OpenRouter), refresh the OpenAI model list. getting-started/ - installation.md: PortableGit (not MinGit) is what the Windows installer fetches; document the 32-bit MinGit fallback. - installation.md / termux.md: installer prefers .[termux-all] then falls back to .[termux]. - nix-setup.md: Python 3.12 (not 3.11), Node.js 22 (not 20); fix invalid 'nix flake update --flake' invocation. - updating.md: 'hermes backup restore --state pre-update' doesn't exist — point at the snapshot/quick-snapshot flow; correct config key 'updates.pre_update_backup' (was 'update.backup'). user-guide/ - configuration.md: api_max_retries default 3 (not 2); display.runtime_footer is the real key (not display.runtime_metadata_footer); checkpoints defaults enabled=false / max_snapshots=20 (not true / 50). - configuring-models.md: 'hermes model list' / 'hermes model set ...' don't exist — hermes model is interactive only. - tui.md: busy_indicator -> tui_status_indicator with values kaomoji|emoji|unicode|ascii (not kawaii|minimal|dots|wings|none). - security.md: SSH backend keys (TERMINAL_SSH_HOST/USER/KEY) live in .env, not config.yaml. - windows-wsl-quickstart.md: there is no 'hermes api' subcommand — the OpenAI-compatible API server runs inside hermes gateway. user-guide/features/ - computer-use.md: approvals.mode (not security.approval_level); fix broken ./browser-use.md link to ./browser.md. - fallback-providers.md: top-level fallback_providers (not model.fallback_providers); the picker is subcommand-based, not modal. - api-server.md: API_SERVER_* are env vars — write to per-profile .env, not 'hermes config set' which targets YAML. - web-search.md: drop web_crawl as a registered tool (it isn't); deep-crawl modes are exposed through web_extract. - kanban.md: failure_limit default is 2, not '~5'. - plugins.md: drop hard-coded '33 providers' count. - honcho.md: fix unclosed quote in echo HONCHO_API_KEY snippet; document that 'hermes honcho' subcommand is gated on memory.provider=honcho; reconcile subcommand list with actual --help output. - memory-providers.md: legacy 'hermes honcho setup' redirect documented. Verified via 'npm run build' — site builds cleanly; broken-link count went from 149 to 146 (no regressions, fixed a few in passing). * docs: round 2 audit fixes + regenerate skill catalogs Follow-up to the previous commit on this branch: Round 2 manual fixes: - quickstart.md: KIMI_CODING_API_KEY mentioned alongside KIMI_API_KEY; voice-mode and ACP install commands rewritten — bare 'pip install ...' doesn't work for curl-installed setups (no pip on PATH, not in repo dir); replaced with 'cd ~/.hermes/hermes-agent && uv pip install -e ".[voice]"'. ACP already ships in [all] so the curl install includes it. - cli.md / configuration.md: 'auxiliary.compression.model' shown as 'google/gemini-3-flash-preview' (the doc's own claimed default); actual default is empty (= use main model). Reworded as 'leave empty (default) or pin a cheap model'. - built-in-plugins.md: added the bundled 'kanban/dashboard' plugin row that was missing from the table. Regenerated skill catalogs: - ran website/scripts/generate-skill-docs.py to refresh all 163 per-skill pages and both reference catalogs (skills-catalog.md, optional-skills-catalog.md). This adds the entries that were genuinely missing — productivity/teams-meeting-pipeline (bundled), optional/finance/* (entire category — 7 skills: 3-statement-model, comps-analysis, dcf-model, excel-author, lbo-model, merger-model, pptx-author), creative/hyperframes, creative/kanban-video-orchestrator, devops/watchers, productivity/shop-app, research/searxng-search, apple/macos-computer-use — and rewrites every other per-skill page from the current SKILL.md. Most diffs are tiny (one line of refreshed metadata). Validation: - 'npm run build' succeeded. - Broken-link count moved 146 -> 155 — the +9 are zh-Hans translation shells that lag every newly-added skill page (pre-existing pattern). No regressions on any en/ page.
2026-05-09 13:19:51 -07:00
| **Model providers** (`plugins/model-providers/`) | All bundled providers under `plugins/model-providers/` discover and register at the first `get_provider_profile()` call. The user picks one at a time via `--provider` or `config.yaml`. |
docs: pluggable surfaces coverage — model-provider guide, full plugin map, opt-in fix (#20749) * docs(providers): add model-provider-plugin authoring guide + fix stale refs New docs: - website/docs/developer-guide/model-provider-plugin.md — full authoring guide (directory layout, minimal example, ProviderProfile fields, overridable hooks, user overrides, api_mode selection, auth types, testing, pip distribution) - Wired into website/sidebars.ts under 'Extending' - Cross-references added in: - guides/build-a-hermes-plugin.md (tip block) - developer-guide/adding-providers.md - developer-guide/provider-runtime.md User guide: - user-guide/features/plugins.md: Plugin types table grows from 3 to 4 with 'Model providers' row Stale comment cleanup (providers/*.py → plugins/model-providers/<name>/): - hermes_cli/main.py:_is_profile_api_key_provider docstring - hermes_cli/doctor.py:_build_apikey_providers_list docstring - hermes_cli/auth.py: PROVIDER_REGISTRY + alias auto-extension comments - hermes_cli/models.py: CANONICAL_PROVIDERS auto-extension comment AGENTS.md: - Project-structure tree: added plugins/model-providers/ row - New section: 'Model-provider plugins' explaining discovery, override semantics, PluginManager integration, kind auto-coerce heuristic Verified: docusaurus build succeeds, new page renders, all 3 cross-links resolve. 347/347 targeted tests pass (tests/providers/, tests/hermes_cli/test_plugins.py, tests/hermes_cli/test_runtime_provider_resolution.py, tests/run_agent/test_provider_parity.py). * docs(plugins): add 'pluggable interfaces at a glance' maps to plugins.md + build-a-hermes-plugin Devs landing on either the user-guide plugin page or the build-a-plugin guide now get an upfront table of every distinct pluggable surface with a link to the right authoring doc. Previously they'd have to read the full general-plugin guide to discover that model providers / platforms / memory / context engines are separate systems. user-guide/features/plugins.md: - New 'Pluggable interfaces — where to go for each' section below the existing 4-kinds table - 10 rows covering every register_* surface (tool, hook, slash command, CLI subcommand, skill, model provider, platform, memory, context engine, image-gen) - Explicit note: TTS/STT are NOT plugin-extensible yet — documented with a pointer to the current config.yaml 'command providers' pattern and a note that register_tts_provider()/register_stt_provider() may come later guides/build-a-hermes-plugin.md: - New :::info 'Not sure which guide you need?' map at the top so devs see all pluggable interfaces before investing in this 737-line general-plugin walkthrough - Existing bottom :::tip expanded to include platform adapters alongside model/memory/context plugins Verified: - All 8 cross-doc links in the new plugins.md table resolve in a docusaurus build (SUCCESS, no new broken links) - TTS link corrected (features/voice → features/tts; latter exists) - Pre-existing broken links/anchors (cron-script-only, llms.txt, adding-platform-adapters#step-by-step-checklist) are unchanged * docs(plugins): correct TTS/STT pluggability \u2014 they ARE plugins (command-providers) Previous commit incorrectly said TTS/STT 'aren't plugin-extensible'. They are, via the config-driven command-provider pattern \u2014 any CLI that reads text and writes audio (or vice versa for STT) is automatically a plugin with zero Python. The tts.md docs cover this extensively and I missed it. plugins.md: - TTS row: 'Config-driven (not a Python plugin)', points at tts.md#custom-command-providers - STT row: points at tts.md#voice-message-transcription-stt (STT docs live in tts.md despite the filename) - Expanded note: TTS/STT use config-driven shell-command templates as their plugin surface (full tts.providers.<name> registry for TTS; HERMES_LOCAL_STT_COMMAND escape hatch for STT) - Any CLI that reads/writes files is automatically a plugin \u2014 no Python register_* API needed - Future register_tts_provider()/register_stt_provider() hooks mentioned as nice-to-have for SDK/streaming cases, not as the primary story build-a-hermes-plugin.md: - Same map update: TTS/STT rows explicit, footer note corrected Verified: - tts.md anchors (custom-command-providers, voice-message-transcription-stt) exist and resolve in docusaurus build (SUCCESS, no new broken links) * docs(plugins): expand pluggable interfaces table with MCP / event hooks / shell hooks / skill taps Broadened the scope beyond Python register_* hooks. Hermes has MULTIPLE plugin-style extension surfaces; they're now all in one table instead of being scattered across feature docs. Added rows for: - **MCP servers** — config.yaml mcp_servers.<name> auto-registers external tools from any MCP server. Huge extensibility surface, previously not linked from the plugin map. - **Gateway event hooks** — drop HOOK.yaml + handler.py into ~/.hermes/hooks/<name>/ to fire on gateway:startup, session:*, agent:*, command:* events. Separate from Python plugin hooks. - **Shell hooks** — hooks: block in config.yaml runs shell commands on events (notifications, auditing, etc.). - **Skill sources (taps)** — hermes skills tap add <repo> to pull in new skill registries beyond the built-in sources. Both docs updated: - user-guide/features/plugins.md: table column renamed to 'How' (mixes Python API + config-driven + drop-in-dir surfaces accurately) - guides/build-a-hermes-plugin.md: :::info map at top mirrors the new surfaces with a forward-link to the consolidated table Note block rewritten: instead of singling out TTS/STT as the 'different style' exception, now honestly describes that Hermes deliberately supports three plugin styles — Python APIs, config-driven commands, and drop-in manifest directories — and devs should pick the one that fits their integration. Not included (considered and rejected): - Transport layer (register_transport) — internal, not user-facing - Tool-call parsers — internal, VLLM phase-2 thing - Cloud browser providers — hardcoded registry, not drop-in yet - Terminal backends — hardcoded if/elif, not drop-in yet - Skill sources (the ABC) — hardcoded list, only taps are user-extensible Verified: - All 5 new anchors resolve (gateway-event-hooks, shell-hooks, skills-hub, custom-command-providers, voice-message-transcription-stt) - Docusaurus build SUCCESS, zero new broken links - Same 3 pre-existing broken links on main (cron-script-only, llms.txt, adding-platform-adapters#step-by-step-checklist) * docs(plugins): cover every pluggable surface in both the overview and how-to Both plugins.md and build-a-hermes-plugin.md now cover every extension surface end-to-end \u2014 general plugin APIs, specialized plugin types, config-driven surfaces \u2014 with concrete authoring patterns for each. plugins.md: - 'What plugins can do' table grows from 9 rows (general ctx.register_* only) to 14 rows covering register_platform, register_image_gen_provider, register_context_engine, MemoryProvider subclass, register_provider (model). Each row links to its full authoring guide. - New 'Plugin sub-categories' section under Plugin Discovery explains how plugins/platforms/, plugins/image_gen/, plugins/memory/, plugins/context_engine/, plugins/model-providers/ are routed to different loaders \u2014 PluginManager vs the per-category own-loader systems. - Explicit mention of user-override semantics at ~/.hermes/plugins/model-providers/ and ~/.hermes/plugins/memory/. build-a-hermes-plugin.md: - New '## Specialized plugin types' section (5 sub-sections): - Model provider plugins \u2014 ProviderProfile + plugin.yaml example, auto-wiring summary, link to full guide - Platform plugins \u2014 BasePlatformAdapter + register_platform() skeleton - Memory provider plugins \u2014 MemoryProvider subclass example - Context engine plugins \u2014 ContextEngine subclass example - Image-generation backends \u2014 ImageGenProvider + kind: backend example - New '## Non-Python extension surfaces' section (5 sub-sections): - MCP servers \u2014 config.yaml mcp_servers.<name> example - Gateway event hooks \u2014 HOOK.yaml + handler.py example - Shell hooks \u2014 hooks: block in config.yaml example - Skill sources (taps) \u2014 hermes skills tap add example - TTS / STT command templates \u2014 tts.providers.<name> with type: command - Distribute via pip / NixOS promoted from ### to ## (they were orphaned after the reorganization) Each specialized / non-Python section has a concrete, copy-pasteable example plus a 'Full guide:' link to the authoritative doc. Devs arriving at the build-a-hermes-plugin guide now see every extension surface at their disposal, not just the general tool/hook/slash-command surface. Verified: - Docusaurus build SUCCESS, zero new broken links - All new cross-links (developer-guide/model-provider-plugin, adding-platform-adapters, memory-provider-plugin, context-engine-plugin, user-guide/features/mcp, skills#skills-hub, hooks#gateway-event-hooks, hooks#shell-hooks, tts#custom-command-providers, tts#voice-message-transcription-stt) resolve - Same 3 pre-existing broken links on main (cron-script-only, llms.txt, adding-platform-adapters#step-by-step-checklist) * docs(plugins): fix opt-in inconsistency — not every plugin is gated The 'Every plugin is disabled by default' statement was wrong. Several plugin categories intentionally bypass plugins.enabled: - Bundled platform plugins (IRC, Teams) auto-load so shipped gateway channels are available out of the box. Activation per channel is via gateway.platforms.<name>.enabled. - Bundled backends (plugins/image_gen/*) auto-load so the default backend 'just works'. Selection via <category>.provider config. - Memory providers are all discovered; one is active via memory.provider. - Context engines are all discovered; one is active via context.engine. - Model providers: all 33 discovered at first get_provider_profile(); user picks via --provider / config. The plugins.enabled allow-list specifically gates: - Standalone plugins (general tools/hooks/slash commands) - User-installed backends - User-installed platforms (third-party gateway adapters) - Pip entry-point backends Which matches the actual code in hermes_cli/plugins.py:737 where the bundled+backend/platform check bypasses the allow-list. Rewrote '## Plugins are opt-in' to: - Retitle to 'Plugins are opt-in (with a few exceptions)' - Narrow opening claim to 'General plugins and user-installed backends are disabled by default' - Added 'What the allow-list does NOT gate' subsection with a full table of which bypass the gate and how they're activated instead - Fixed migration section wording (bundled platform/backend plugins never needed grandfathering) Verified: docusaurus build SUCCESS, zero new broken links.
2026-05-06 07:24:42 -07:00
| **Pip-installed `backend` plugins** | Opt-in via `plugins.enabled` (same as general plugins). |
| **User-installed platforms** (under `~/.hermes/plugins/platforms/`) | Opt-in via `plugins.enabled` — third-party gateway adapters need explicit consent. |
In short: **bundled "always-works" infrastructure loads automatically; third-party general plugins are opt-in.** The `plugins.enabled` allow-list is the gate specifically for arbitrary code a user drops into `~/.hermes/plugins/`.
feat(plugins): make all plugins opt-in by default Plugins now require explicit consent to load. Discovery still finds every plugin — user-installed, bundled, and pip — so they all show up in `hermes plugins` and `/plugins`, but the loader only instantiates plugins whose name appears in `plugins.enabled` in config.yaml. This removes the previous ambient-execution risk where a newly-installed or bundled plugin could register hooks, tools, and commands on first run without the user opting in. The three-state model is now explicit: enabled — in plugins.enabled, loads on next session disabled — in plugins.disabled, never loads (wins over enabled) not enabled — discovered but never opted in (default for new installs) `hermes plugins install <repo>` prompts "Enable 'name' now? [y/N]" (defaults to no). New `--enable` / `--no-enable` flags skip the prompt for scripted installs. `hermes plugins enable/disable` manage both lists so a disabled plugin stays explicitly off even if something later adds it to enabled. Config migration (schema v20 → v21): existing user plugins already installed under ~/.hermes/plugins/ (minus anything in plugins.disabled) are auto-grandfathered into plugins.enabled so upgrades don't silently break working setups. Bundled plugins are NOT grandfathered — even existing users have to opt in explicitly. Also: HERMES_DISABLE_BUNDLED_PLUGINS env var removed (redundant with opt-in default), cmd_list now shows bundled + user plugins together with their three-state status, interactive UI tags bundled entries [bundled], docs updated across plugins.md and built-in-plugins.md. Validation: 442 plugin/config tests pass. E2E: fresh install discovers disk-cleanup but does not load it; `hermes plugins enable disk-cleanup` activates hooks; migration grandfathers existing user plugins correctly while leaving bundled plugins off.
2026-04-20 04:40:17 -07:00
### Migration for existing users
docs: pluggable surfaces coverage — model-provider guide, full plugin map, opt-in fix (#20749) * docs(providers): add model-provider-plugin authoring guide + fix stale refs New docs: - website/docs/developer-guide/model-provider-plugin.md — full authoring guide (directory layout, minimal example, ProviderProfile fields, overridable hooks, user overrides, api_mode selection, auth types, testing, pip distribution) - Wired into website/sidebars.ts under 'Extending' - Cross-references added in: - guides/build-a-hermes-plugin.md (tip block) - developer-guide/adding-providers.md - developer-guide/provider-runtime.md User guide: - user-guide/features/plugins.md: Plugin types table grows from 3 to 4 with 'Model providers' row Stale comment cleanup (providers/*.py → plugins/model-providers/<name>/): - hermes_cli/main.py:_is_profile_api_key_provider docstring - hermes_cli/doctor.py:_build_apikey_providers_list docstring - hermes_cli/auth.py: PROVIDER_REGISTRY + alias auto-extension comments - hermes_cli/models.py: CANONICAL_PROVIDERS auto-extension comment AGENTS.md: - Project-structure tree: added plugins/model-providers/ row - New section: 'Model-provider plugins' explaining discovery, override semantics, PluginManager integration, kind auto-coerce heuristic Verified: docusaurus build succeeds, new page renders, all 3 cross-links resolve. 347/347 targeted tests pass (tests/providers/, tests/hermes_cli/test_plugins.py, tests/hermes_cli/test_runtime_provider_resolution.py, tests/run_agent/test_provider_parity.py). * docs(plugins): add 'pluggable interfaces at a glance' maps to plugins.md + build-a-hermes-plugin Devs landing on either the user-guide plugin page or the build-a-plugin guide now get an upfront table of every distinct pluggable surface with a link to the right authoring doc. Previously they'd have to read the full general-plugin guide to discover that model providers / platforms / memory / context engines are separate systems. user-guide/features/plugins.md: - New 'Pluggable interfaces — where to go for each' section below the existing 4-kinds table - 10 rows covering every register_* surface (tool, hook, slash command, CLI subcommand, skill, model provider, platform, memory, context engine, image-gen) - Explicit note: TTS/STT are NOT plugin-extensible yet — documented with a pointer to the current config.yaml 'command providers' pattern and a note that register_tts_provider()/register_stt_provider() may come later guides/build-a-hermes-plugin.md: - New :::info 'Not sure which guide you need?' map at the top so devs see all pluggable interfaces before investing in this 737-line general-plugin walkthrough - Existing bottom :::tip expanded to include platform adapters alongside model/memory/context plugins Verified: - All 8 cross-doc links in the new plugins.md table resolve in a docusaurus build (SUCCESS, no new broken links) - TTS link corrected (features/voice → features/tts; latter exists) - Pre-existing broken links/anchors (cron-script-only, llms.txt, adding-platform-adapters#step-by-step-checklist) are unchanged * docs(plugins): correct TTS/STT pluggability \u2014 they ARE plugins (command-providers) Previous commit incorrectly said TTS/STT 'aren't plugin-extensible'. They are, via the config-driven command-provider pattern \u2014 any CLI that reads text and writes audio (or vice versa for STT) is automatically a plugin with zero Python. The tts.md docs cover this extensively and I missed it. plugins.md: - TTS row: 'Config-driven (not a Python plugin)', points at tts.md#custom-command-providers - STT row: points at tts.md#voice-message-transcription-stt (STT docs live in tts.md despite the filename) - Expanded note: TTS/STT use config-driven shell-command templates as their plugin surface (full tts.providers.<name> registry for TTS; HERMES_LOCAL_STT_COMMAND escape hatch for STT) - Any CLI that reads/writes files is automatically a plugin \u2014 no Python register_* API needed - Future register_tts_provider()/register_stt_provider() hooks mentioned as nice-to-have for SDK/streaming cases, not as the primary story build-a-hermes-plugin.md: - Same map update: TTS/STT rows explicit, footer note corrected Verified: - tts.md anchors (custom-command-providers, voice-message-transcription-stt) exist and resolve in docusaurus build (SUCCESS, no new broken links) * docs(plugins): expand pluggable interfaces table with MCP / event hooks / shell hooks / skill taps Broadened the scope beyond Python register_* hooks. Hermes has MULTIPLE plugin-style extension surfaces; they're now all in one table instead of being scattered across feature docs. Added rows for: - **MCP servers** — config.yaml mcp_servers.<name> auto-registers external tools from any MCP server. Huge extensibility surface, previously not linked from the plugin map. - **Gateway event hooks** — drop HOOK.yaml + handler.py into ~/.hermes/hooks/<name>/ to fire on gateway:startup, session:*, agent:*, command:* events. Separate from Python plugin hooks. - **Shell hooks** — hooks: block in config.yaml runs shell commands on events (notifications, auditing, etc.). - **Skill sources (taps)** — hermes skills tap add <repo> to pull in new skill registries beyond the built-in sources. Both docs updated: - user-guide/features/plugins.md: table column renamed to 'How' (mixes Python API + config-driven + drop-in-dir surfaces accurately) - guides/build-a-hermes-plugin.md: :::info map at top mirrors the new surfaces with a forward-link to the consolidated table Note block rewritten: instead of singling out TTS/STT as the 'different style' exception, now honestly describes that Hermes deliberately supports three plugin styles — Python APIs, config-driven commands, and drop-in manifest directories — and devs should pick the one that fits their integration. Not included (considered and rejected): - Transport layer (register_transport) — internal, not user-facing - Tool-call parsers — internal, VLLM phase-2 thing - Cloud browser providers — hardcoded registry, not drop-in yet - Terminal backends — hardcoded if/elif, not drop-in yet - Skill sources (the ABC) — hardcoded list, only taps are user-extensible Verified: - All 5 new anchors resolve (gateway-event-hooks, shell-hooks, skills-hub, custom-command-providers, voice-message-transcription-stt) - Docusaurus build SUCCESS, zero new broken links - Same 3 pre-existing broken links on main (cron-script-only, llms.txt, adding-platform-adapters#step-by-step-checklist) * docs(plugins): cover every pluggable surface in both the overview and how-to Both plugins.md and build-a-hermes-plugin.md now cover every extension surface end-to-end \u2014 general plugin APIs, specialized plugin types, config-driven surfaces \u2014 with concrete authoring patterns for each. plugins.md: - 'What plugins can do' table grows from 9 rows (general ctx.register_* only) to 14 rows covering register_platform, register_image_gen_provider, register_context_engine, MemoryProvider subclass, register_provider (model). Each row links to its full authoring guide. - New 'Plugin sub-categories' section under Plugin Discovery explains how plugins/platforms/, plugins/image_gen/, plugins/memory/, plugins/context_engine/, plugins/model-providers/ are routed to different loaders \u2014 PluginManager vs the per-category own-loader systems. - Explicit mention of user-override semantics at ~/.hermes/plugins/model-providers/ and ~/.hermes/plugins/memory/. build-a-hermes-plugin.md: - New '## Specialized plugin types' section (5 sub-sections): - Model provider plugins \u2014 ProviderProfile + plugin.yaml example, auto-wiring summary, link to full guide - Platform plugins \u2014 BasePlatformAdapter + register_platform() skeleton - Memory provider plugins \u2014 MemoryProvider subclass example - Context engine plugins \u2014 ContextEngine subclass example - Image-generation backends \u2014 ImageGenProvider + kind: backend example - New '## Non-Python extension surfaces' section (5 sub-sections): - MCP servers \u2014 config.yaml mcp_servers.<name> example - Gateway event hooks \u2014 HOOK.yaml + handler.py example - Shell hooks \u2014 hooks: block in config.yaml example - Skill sources (taps) \u2014 hermes skills tap add example - TTS / STT command templates \u2014 tts.providers.<name> with type: command - Distribute via pip / NixOS promoted from ### to ## (they were orphaned after the reorganization) Each specialized / non-Python section has a concrete, copy-pasteable example plus a 'Full guide:' link to the authoritative doc. Devs arriving at the build-a-hermes-plugin guide now see every extension surface at their disposal, not just the general tool/hook/slash-command surface. Verified: - Docusaurus build SUCCESS, zero new broken links - All new cross-links (developer-guide/model-provider-plugin, adding-platform-adapters, memory-provider-plugin, context-engine-plugin, user-guide/features/mcp, skills#skills-hub, hooks#gateway-event-hooks, hooks#shell-hooks, tts#custom-command-providers, tts#voice-message-transcription-stt) resolve - Same 3 pre-existing broken links on main (cron-script-only, llms.txt, adding-platform-adapters#step-by-step-checklist) * docs(plugins): fix opt-in inconsistency — not every plugin is gated The 'Every plugin is disabled by default' statement was wrong. Several plugin categories intentionally bypass plugins.enabled: - Bundled platform plugins (IRC, Teams) auto-load so shipped gateway channels are available out of the box. Activation per channel is via gateway.platforms.<name>.enabled. - Bundled backends (plugins/image_gen/*) auto-load so the default backend 'just works'. Selection via <category>.provider config. - Memory providers are all discovered; one is active via memory.provider. - Context engines are all discovered; one is active via context.engine. - Model providers: all 33 discovered at first get_provider_profile(); user picks via --provider / config. The plugins.enabled allow-list specifically gates: - Standalone plugins (general tools/hooks/slash commands) - User-installed backends - User-installed platforms (third-party gateway adapters) - Pip entry-point backends Which matches the actual code in hermes_cli/plugins.py:737 where the bundled+backend/platform check bypasses the allow-list. Rewrote '## Plugins are opt-in' to: - Retitle to 'Plugins are opt-in (with a few exceptions)' - Narrow opening claim to 'General plugins and user-installed backends are disabled by default' - Added 'What the allow-list does NOT gate' subsection with a full table of which bypass the gate and how they're activated instead - Fixed migration section wording (bundled platform/backend plugins never needed grandfathering) Verified: docusaurus build SUCCESS, zero new broken links.
2026-05-06 07:24:42 -07:00
When you upgrade to a version of Hermes that has opt-in plugins (config schema v21+), any user plugins already installed under `~/.hermes/plugins/` that weren't already in `plugins.disabled` are **automatically grandfathered** into `plugins.enabled`. Your existing setup keeps working. Bundled standalone plugins are NOT grandfathered — even existing users have to opt in explicitly. (Bundled platform/backend plugins never needed grandfathering because they were never gated.)
## Available hooks
Plugins can register callbacks for these lifecycle events. See the **[Event Hooks page](/docs/user-guide/features/hooks#plugin-hooks)** for full details, callback signatures, and examples.
| Hook | Fires when |
|------|-----------|
| [`pre_tool_call`](/docs/user-guide/features/hooks#pre_tool_call) | Before any tool executes |
| [`post_tool_call`](/docs/user-guide/features/hooks#post_tool_call) | After any tool returns |
| [`pre_llm_call`](/docs/user-guide/features/hooks#pre_llm_call) | Once per turn, before the LLM loop — can return `{"context": "..."}` to [inject context into the user message](/docs/user-guide/features/hooks#pre_llm_call) |
| [`post_llm_call`](/docs/user-guide/features/hooks#post_llm_call) | Once per turn, after the LLM loop (successful turns only) |
| [`on_session_start`](/docs/user-guide/features/hooks#on_session_start) | New session created (first turn only) |
| [`on_session_end`](/docs/user-guide/features/hooks#on_session_end) | End of every `run_conversation` call + CLI exit handler |
docs: resync reference, user-guide, developer-guide, and messaging pages against code (#17738) Broad drift audit against origin/main (b52b63396). Reference pages (most user-visible drift): - slash-commands: add /busy, /curator, /footer, /indicator, /redraw, /steer that were missing; drop non-existent /terminal-setup; fix /q footnote (resolves to /queue, not /quit); extend CLI-only list with all 24 CLI-only commands in the registry - cli-commands: add dedicated sections for hermes curator / fallback / hooks (new subcommands not previously documented); remove stale hermes honcho standalone section (the plugin registers dynamically via hermes memory); list curator/fallback/hooks in top-level table; fix completion to include fish - toolsets-reference: document the real 52-toolset count; split browser vs browser-cdp; add discord / discord_admin / spotify / yuanbao; correct hermes-cli tool count from 36 to 38; fix misleading claim that hermes-homeassistant adds tools (it's identical to hermes-cli) - tools-reference: bump tool count 55 -> 68; add 7 Spotify, 5 Yuanbao, 2 Discord toolsets; move browser_cdp/browser_dialog to their own browser-cdp toolset section - environment-variables: add 40+ user-facing HERMES_* vars that were undocumented (--yolo, --accept-hooks, --ignore-*, inference model override, agent/stream/checkpoint timeouts, OAuth trace, per-platform batch tuning for Telegram/Discord/Matrix/Feishu/WeCom, cron knobs, gateway restart/connect timeouts); dedupe the Cron Scheduler section; replace stale QQ_SANDBOX with QQ_PORTAL_HOST User-guide (top level): - cli.md: compression preserves last 20 turns, not 4 (protect_last_n: 20) - configuration.md: display.platforms is the canonical per-platform override key; tool_progress_overrides is deprecated and auto-migrated - profiles.md: model.default is the config key, not model.model - sessions.md: CLI/TUI session IDs use 6-char hex, gateway uses 8 - checkpoints-and-rollback.md: destructive-command list now matches _DESTRUCTIVE_PATTERNS (adds rmdir, cp, install, dd) - docker.md: the container runs as non-root hermes (UID 10000) via gosu; fix install command (uv pip); add missing --insecure on the dashboard compose example (required for non-loopback bind) - security.md: systemctl danger pattern also matches 'restart' - index.md: built-in tool count 47 -> 68 - integrations/index.md: 6 STT providers, 8 memory providers - integrations/providers.md: drop fictional dashscope/qwen aliases Features: - overview.md: 9 image models (not 8), 9 TTS providers (not 5), 8 memory providers (Supermemory was missing) - tool-gateway.md: 9 image models - tools.md: extend common-toolsets list with search / messaging / spotify / discord / debugging / safe - fallback-providers.md: add 6 real providers from PROVIDER_REGISTRY (lmstudio, kimi-coding-cn, stepfun, alibaba-coding-plan, tencent-tokenhub, azure-foundry) - plugins.md: Available Hooks table now includes on_session_finalize, on_session_reset, subagent_stop - built-in-plugins.md: add the 7 bundled plugins the page didn't mention (spotify, google_meet, three image_gen providers, two dashboard examples) - web-dashboard.md: add --insecure and --tui flags - cron.md: hermes cron create takes positional schedule/prompt, not flags Messaging: - telegram.md: TELEGRAM_WEBHOOK_SECRET is now REQUIRED when TELEGRAM_WEBHOOK_URL is set (gateway refuses to start without it per GHSA-3vpc-7q5r-276h). Biggest user-visible drift in the batch. - discord.md: HERMES_DISCORD_TEXT_BATCH_SPLIT_DELAY_SECONDS default is 2.0, not 0.1 - dingtalk.md: document DINGTALK_REQUIRE_MENTION / FREE_RESPONSE_CHATS / MENTION_PATTERNS / HOME_CHANNEL / ALLOW_ALL_USERS that the adapter supports - bluebubbles.md: drop fictional BLUEBUBBLES_SEND_READ_RECEIPTS env var; the setting lives in platforms.bluebubbles.extra only - qqbot.md: drop dead QQ_SANDBOX; add real QQ_PORTAL_HOST and QQ_GROUP_ALLOWED_USERS - wecom-callback.md: replace 'hermes gateway start' (service-only) with 'hermes gateway' for first-time setup Developer-guide: - architecture.md: refresh tool/toolset counts (61/52), terminal backend count (7), line counts for run_agent.py (~13.7k), cli.py (~11.5k), main.py (~10.4k), setup.py (~3.5k), gateway/run.py (~12.2k), mcp_tool.py (~3.1k); add yuanbao adapter, bump platform adapter count 18 -> 20 - agent-loop.md: run_agent.py line count 10.7k -> 13.7k - tools-runtime.md: add vercel_sandbox backend - adding-tools.md: remove stale 'Discovery import added to model_tools.py' checklist item (registry auto-discovery) - adding-platform-adapters.md: mark send_typing / get_chat_info as concrete base methods; only connect/disconnect/send are abstract - acp-internals.md: ACP sessions now persist to SessionDB (~/.hermes/state.db); acp.run_agent call uses use_unstable_protocol=True - cron-internals.md: gateway runs scheduler in a dedicated background thread via _start_cron_ticker, not on a maintenance cycle; locking is cross-process via fcntl.flock (Unix) / msvcrt.locking (Windows) - gateway-internals.md: gateway/run.py ~12k lines - provider-runtime.md: cron DOES support fallback (run_job reads fallback_providers from config) - session-storage.md: SCHEMA_VERSION = 11 (not 9); add migrations 10 and 11 (trigram FTS, inline-mode FTS5 re-index); add api_call_count column to Sessions DDL; document messages_fts_trigram and state_meta in the architecture tree - context-compression-and-caching.md: remove the obsolete 'context pressure warnings' section (warnings were removed for causing models to give up early) - context-engine-plugin.md: compress() signature now includes focus_topic param - extending-the-cli.md: _build_tui_layout_children signature now includes model_picker_widget; add to default layout Also fixed three pre-existing broken links/anchors the build warned about (docker.md -> api-server.md, yuanbao.md -> cron-jobs.md and tips#background-tasks, nix-setup.md -> #container-aware-cli). Regenerated per-skill pages via website/scripts/generate-skill-docs.py so catalog tables and sidebar are consistent with current SKILL.md frontmatter. docusaurus build: clean, no broken links or anchors.
2026-04-29 20:55:59 -07:00
| [`on_session_finalize`](/docs/user-guide/features/hooks#on_session_finalize) | CLI/gateway tears down an active session (`/new`, GC, CLI quit) |
| [`on_session_reset`](/docs/user-guide/features/hooks#on_session_reset) | Gateway swaps in a new session key (`/new`, `/reset`, `/clear`, idle rotation) |
| [`subagent_stop`](/docs/user-guide/features/hooks#subagent_stop) | Once per child after `delegate_task` finishes |
| [`pre_gateway_dispatch`](/docs/user-guide/features/hooks#pre_gateway_dispatch) | Gateway received a user message, before auth + dispatch. Return `{"action": "skip" \| "rewrite" \| "allow", ...}` to influence flow. |
## Plugin types
docs: pluggable surfaces coverage — model-provider guide, full plugin map, opt-in fix (#20749) * docs(providers): add model-provider-plugin authoring guide + fix stale refs New docs: - website/docs/developer-guide/model-provider-plugin.md — full authoring guide (directory layout, minimal example, ProviderProfile fields, overridable hooks, user overrides, api_mode selection, auth types, testing, pip distribution) - Wired into website/sidebars.ts under 'Extending' - Cross-references added in: - guides/build-a-hermes-plugin.md (tip block) - developer-guide/adding-providers.md - developer-guide/provider-runtime.md User guide: - user-guide/features/plugins.md: Plugin types table grows from 3 to 4 with 'Model providers' row Stale comment cleanup (providers/*.py → plugins/model-providers/<name>/): - hermes_cli/main.py:_is_profile_api_key_provider docstring - hermes_cli/doctor.py:_build_apikey_providers_list docstring - hermes_cli/auth.py: PROVIDER_REGISTRY + alias auto-extension comments - hermes_cli/models.py: CANONICAL_PROVIDERS auto-extension comment AGENTS.md: - Project-structure tree: added plugins/model-providers/ row - New section: 'Model-provider plugins' explaining discovery, override semantics, PluginManager integration, kind auto-coerce heuristic Verified: docusaurus build succeeds, new page renders, all 3 cross-links resolve. 347/347 targeted tests pass (tests/providers/, tests/hermes_cli/test_plugins.py, tests/hermes_cli/test_runtime_provider_resolution.py, tests/run_agent/test_provider_parity.py). * docs(plugins): add 'pluggable interfaces at a glance' maps to plugins.md + build-a-hermes-plugin Devs landing on either the user-guide plugin page or the build-a-plugin guide now get an upfront table of every distinct pluggable surface with a link to the right authoring doc. Previously they'd have to read the full general-plugin guide to discover that model providers / platforms / memory / context engines are separate systems. user-guide/features/plugins.md: - New 'Pluggable interfaces — where to go for each' section below the existing 4-kinds table - 10 rows covering every register_* surface (tool, hook, slash command, CLI subcommand, skill, model provider, platform, memory, context engine, image-gen) - Explicit note: TTS/STT are NOT plugin-extensible yet — documented with a pointer to the current config.yaml 'command providers' pattern and a note that register_tts_provider()/register_stt_provider() may come later guides/build-a-hermes-plugin.md: - New :::info 'Not sure which guide you need?' map at the top so devs see all pluggable interfaces before investing in this 737-line general-plugin walkthrough - Existing bottom :::tip expanded to include platform adapters alongside model/memory/context plugins Verified: - All 8 cross-doc links in the new plugins.md table resolve in a docusaurus build (SUCCESS, no new broken links) - TTS link corrected (features/voice → features/tts; latter exists) - Pre-existing broken links/anchors (cron-script-only, llms.txt, adding-platform-adapters#step-by-step-checklist) are unchanged * docs(plugins): correct TTS/STT pluggability \u2014 they ARE plugins (command-providers) Previous commit incorrectly said TTS/STT 'aren't plugin-extensible'. They are, via the config-driven command-provider pattern \u2014 any CLI that reads text and writes audio (or vice versa for STT) is automatically a plugin with zero Python. The tts.md docs cover this extensively and I missed it. plugins.md: - TTS row: 'Config-driven (not a Python plugin)', points at tts.md#custom-command-providers - STT row: points at tts.md#voice-message-transcription-stt (STT docs live in tts.md despite the filename) - Expanded note: TTS/STT use config-driven shell-command templates as their plugin surface (full tts.providers.<name> registry for TTS; HERMES_LOCAL_STT_COMMAND escape hatch for STT) - Any CLI that reads/writes files is automatically a plugin \u2014 no Python register_* API needed - Future register_tts_provider()/register_stt_provider() hooks mentioned as nice-to-have for SDK/streaming cases, not as the primary story build-a-hermes-plugin.md: - Same map update: TTS/STT rows explicit, footer note corrected Verified: - tts.md anchors (custom-command-providers, voice-message-transcription-stt) exist and resolve in docusaurus build (SUCCESS, no new broken links) * docs(plugins): expand pluggable interfaces table with MCP / event hooks / shell hooks / skill taps Broadened the scope beyond Python register_* hooks. Hermes has MULTIPLE plugin-style extension surfaces; they're now all in one table instead of being scattered across feature docs. Added rows for: - **MCP servers** — config.yaml mcp_servers.<name> auto-registers external tools from any MCP server. Huge extensibility surface, previously not linked from the plugin map. - **Gateway event hooks** — drop HOOK.yaml + handler.py into ~/.hermes/hooks/<name>/ to fire on gateway:startup, session:*, agent:*, command:* events. Separate from Python plugin hooks. - **Shell hooks** — hooks: block in config.yaml runs shell commands on events (notifications, auditing, etc.). - **Skill sources (taps)** — hermes skills tap add <repo> to pull in new skill registries beyond the built-in sources. Both docs updated: - user-guide/features/plugins.md: table column renamed to 'How' (mixes Python API + config-driven + drop-in-dir surfaces accurately) - guides/build-a-hermes-plugin.md: :::info map at top mirrors the new surfaces with a forward-link to the consolidated table Note block rewritten: instead of singling out TTS/STT as the 'different style' exception, now honestly describes that Hermes deliberately supports three plugin styles — Python APIs, config-driven commands, and drop-in manifest directories — and devs should pick the one that fits their integration. Not included (considered and rejected): - Transport layer (register_transport) — internal, not user-facing - Tool-call parsers — internal, VLLM phase-2 thing - Cloud browser providers — hardcoded registry, not drop-in yet - Terminal backends — hardcoded if/elif, not drop-in yet - Skill sources (the ABC) — hardcoded list, only taps are user-extensible Verified: - All 5 new anchors resolve (gateway-event-hooks, shell-hooks, skills-hub, custom-command-providers, voice-message-transcription-stt) - Docusaurus build SUCCESS, zero new broken links - Same 3 pre-existing broken links on main (cron-script-only, llms.txt, adding-platform-adapters#step-by-step-checklist) * docs(plugins): cover every pluggable surface in both the overview and how-to Both plugins.md and build-a-hermes-plugin.md now cover every extension surface end-to-end \u2014 general plugin APIs, specialized plugin types, config-driven surfaces \u2014 with concrete authoring patterns for each. plugins.md: - 'What plugins can do' table grows from 9 rows (general ctx.register_* only) to 14 rows covering register_platform, register_image_gen_provider, register_context_engine, MemoryProvider subclass, register_provider (model). Each row links to its full authoring guide. - New 'Plugin sub-categories' section under Plugin Discovery explains how plugins/platforms/, plugins/image_gen/, plugins/memory/, plugins/context_engine/, plugins/model-providers/ are routed to different loaders \u2014 PluginManager vs the per-category own-loader systems. - Explicit mention of user-override semantics at ~/.hermes/plugins/model-providers/ and ~/.hermes/plugins/memory/. build-a-hermes-plugin.md: - New '## Specialized plugin types' section (5 sub-sections): - Model provider plugins \u2014 ProviderProfile + plugin.yaml example, auto-wiring summary, link to full guide - Platform plugins \u2014 BasePlatformAdapter + register_platform() skeleton - Memory provider plugins \u2014 MemoryProvider subclass example - Context engine plugins \u2014 ContextEngine subclass example - Image-generation backends \u2014 ImageGenProvider + kind: backend example - New '## Non-Python extension surfaces' section (5 sub-sections): - MCP servers \u2014 config.yaml mcp_servers.<name> example - Gateway event hooks \u2014 HOOK.yaml + handler.py example - Shell hooks \u2014 hooks: block in config.yaml example - Skill sources (taps) \u2014 hermes skills tap add example - TTS / STT command templates \u2014 tts.providers.<name> with type: command - Distribute via pip / NixOS promoted from ### to ## (they were orphaned after the reorganization) Each specialized / non-Python section has a concrete, copy-pasteable example plus a 'Full guide:' link to the authoritative doc. Devs arriving at the build-a-hermes-plugin guide now see every extension surface at their disposal, not just the general tool/hook/slash-command surface. Verified: - Docusaurus build SUCCESS, zero new broken links - All new cross-links (developer-guide/model-provider-plugin, adding-platform-adapters, memory-provider-plugin, context-engine-plugin, user-guide/features/mcp, skills#skills-hub, hooks#gateway-event-hooks, hooks#shell-hooks, tts#custom-command-providers, tts#voice-message-transcription-stt) resolve - Same 3 pre-existing broken links on main (cron-script-only, llms.txt, adding-platform-adapters#step-by-step-checklist) * docs(plugins): fix opt-in inconsistency — not every plugin is gated The 'Every plugin is disabled by default' statement was wrong. Several plugin categories intentionally bypass plugins.enabled: - Bundled platform plugins (IRC, Teams) auto-load so shipped gateway channels are available out of the box. Activation per channel is via gateway.platforms.<name>.enabled. - Bundled backends (plugins/image_gen/*) auto-load so the default backend 'just works'. Selection via <category>.provider config. - Memory providers are all discovered; one is active via memory.provider. - Context engines are all discovered; one is active via context.engine. - Model providers: all 33 discovered at first get_provider_profile(); user picks via --provider / config. The plugins.enabled allow-list specifically gates: - Standalone plugins (general tools/hooks/slash commands) - User-installed backends - User-installed platforms (third-party gateway adapters) - Pip entry-point backends Which matches the actual code in hermes_cli/plugins.py:737 where the bundled+backend/platform check bypasses the allow-list. Rewrote '## Plugins are opt-in' to: - Retitle to 'Plugins are opt-in (with a few exceptions)' - Narrow opening claim to 'General plugins and user-installed backends are disabled by default' - Added 'What the allow-list does NOT gate' subsection with a full table of which bypass the gate and how they're activated instead - Fixed migration section wording (bundled platform/backend plugins never needed grandfathering) Verified: docusaurus build SUCCESS, zero new broken links.
2026-05-06 07:24:42 -07:00
Hermes has four kinds of plugins:
| Type | What it does | Selection | Location |
|------|-------------|-----------|----------|
| **General plugins** | Add tools, hooks, slash commands, CLI commands | Multi-select (enable/disable) | `~/.hermes/plugins/` |
| **Memory providers** | Replace or augment built-in memory | Single-select (one active) | `plugins/memory/` |
| **Context engines** | Replace the built-in context compressor | Single-select (one active) | `plugins/context_engine/` |
docs: pluggable surfaces coverage — model-provider guide, full plugin map, opt-in fix (#20749) * docs(providers): add model-provider-plugin authoring guide + fix stale refs New docs: - website/docs/developer-guide/model-provider-plugin.md — full authoring guide (directory layout, minimal example, ProviderProfile fields, overridable hooks, user overrides, api_mode selection, auth types, testing, pip distribution) - Wired into website/sidebars.ts under 'Extending' - Cross-references added in: - guides/build-a-hermes-plugin.md (tip block) - developer-guide/adding-providers.md - developer-guide/provider-runtime.md User guide: - user-guide/features/plugins.md: Plugin types table grows from 3 to 4 with 'Model providers' row Stale comment cleanup (providers/*.py → plugins/model-providers/<name>/): - hermes_cli/main.py:_is_profile_api_key_provider docstring - hermes_cli/doctor.py:_build_apikey_providers_list docstring - hermes_cli/auth.py: PROVIDER_REGISTRY + alias auto-extension comments - hermes_cli/models.py: CANONICAL_PROVIDERS auto-extension comment AGENTS.md: - Project-structure tree: added plugins/model-providers/ row - New section: 'Model-provider plugins' explaining discovery, override semantics, PluginManager integration, kind auto-coerce heuristic Verified: docusaurus build succeeds, new page renders, all 3 cross-links resolve. 347/347 targeted tests pass (tests/providers/, tests/hermes_cli/test_plugins.py, tests/hermes_cli/test_runtime_provider_resolution.py, tests/run_agent/test_provider_parity.py). * docs(plugins): add 'pluggable interfaces at a glance' maps to plugins.md + build-a-hermes-plugin Devs landing on either the user-guide plugin page or the build-a-plugin guide now get an upfront table of every distinct pluggable surface with a link to the right authoring doc. Previously they'd have to read the full general-plugin guide to discover that model providers / platforms / memory / context engines are separate systems. user-guide/features/plugins.md: - New 'Pluggable interfaces — where to go for each' section below the existing 4-kinds table - 10 rows covering every register_* surface (tool, hook, slash command, CLI subcommand, skill, model provider, platform, memory, context engine, image-gen) - Explicit note: TTS/STT are NOT plugin-extensible yet — documented with a pointer to the current config.yaml 'command providers' pattern and a note that register_tts_provider()/register_stt_provider() may come later guides/build-a-hermes-plugin.md: - New :::info 'Not sure which guide you need?' map at the top so devs see all pluggable interfaces before investing in this 737-line general-plugin walkthrough - Existing bottom :::tip expanded to include platform adapters alongside model/memory/context plugins Verified: - All 8 cross-doc links in the new plugins.md table resolve in a docusaurus build (SUCCESS, no new broken links) - TTS link corrected (features/voice → features/tts; latter exists) - Pre-existing broken links/anchors (cron-script-only, llms.txt, adding-platform-adapters#step-by-step-checklist) are unchanged * docs(plugins): correct TTS/STT pluggability \u2014 they ARE plugins (command-providers) Previous commit incorrectly said TTS/STT 'aren't plugin-extensible'. They are, via the config-driven command-provider pattern \u2014 any CLI that reads text and writes audio (or vice versa for STT) is automatically a plugin with zero Python. The tts.md docs cover this extensively and I missed it. plugins.md: - TTS row: 'Config-driven (not a Python plugin)', points at tts.md#custom-command-providers - STT row: points at tts.md#voice-message-transcription-stt (STT docs live in tts.md despite the filename) - Expanded note: TTS/STT use config-driven shell-command templates as their plugin surface (full tts.providers.<name> registry for TTS; HERMES_LOCAL_STT_COMMAND escape hatch for STT) - Any CLI that reads/writes files is automatically a plugin \u2014 no Python register_* API needed - Future register_tts_provider()/register_stt_provider() hooks mentioned as nice-to-have for SDK/streaming cases, not as the primary story build-a-hermes-plugin.md: - Same map update: TTS/STT rows explicit, footer note corrected Verified: - tts.md anchors (custom-command-providers, voice-message-transcription-stt) exist and resolve in docusaurus build (SUCCESS, no new broken links) * docs(plugins): expand pluggable interfaces table with MCP / event hooks / shell hooks / skill taps Broadened the scope beyond Python register_* hooks. Hermes has MULTIPLE plugin-style extension surfaces; they're now all in one table instead of being scattered across feature docs. Added rows for: - **MCP servers** — config.yaml mcp_servers.<name> auto-registers external tools from any MCP server. Huge extensibility surface, previously not linked from the plugin map. - **Gateway event hooks** — drop HOOK.yaml + handler.py into ~/.hermes/hooks/<name>/ to fire on gateway:startup, session:*, agent:*, command:* events. Separate from Python plugin hooks. - **Shell hooks** — hooks: block in config.yaml runs shell commands on events (notifications, auditing, etc.). - **Skill sources (taps)** — hermes skills tap add <repo> to pull in new skill registries beyond the built-in sources. Both docs updated: - user-guide/features/plugins.md: table column renamed to 'How' (mixes Python API + config-driven + drop-in-dir surfaces accurately) - guides/build-a-hermes-plugin.md: :::info map at top mirrors the new surfaces with a forward-link to the consolidated table Note block rewritten: instead of singling out TTS/STT as the 'different style' exception, now honestly describes that Hermes deliberately supports three plugin styles — Python APIs, config-driven commands, and drop-in manifest directories — and devs should pick the one that fits their integration. Not included (considered and rejected): - Transport layer (register_transport) — internal, not user-facing - Tool-call parsers — internal, VLLM phase-2 thing - Cloud browser providers — hardcoded registry, not drop-in yet - Terminal backends — hardcoded if/elif, not drop-in yet - Skill sources (the ABC) — hardcoded list, only taps are user-extensible Verified: - All 5 new anchors resolve (gateway-event-hooks, shell-hooks, skills-hub, custom-command-providers, voice-message-transcription-stt) - Docusaurus build SUCCESS, zero new broken links - Same 3 pre-existing broken links on main (cron-script-only, llms.txt, adding-platform-adapters#step-by-step-checklist) * docs(plugins): cover every pluggable surface in both the overview and how-to Both plugins.md and build-a-hermes-plugin.md now cover every extension surface end-to-end \u2014 general plugin APIs, specialized plugin types, config-driven surfaces \u2014 with concrete authoring patterns for each. plugins.md: - 'What plugins can do' table grows from 9 rows (general ctx.register_* only) to 14 rows covering register_platform, register_image_gen_provider, register_context_engine, MemoryProvider subclass, register_provider (model). Each row links to its full authoring guide. - New 'Plugin sub-categories' section under Plugin Discovery explains how plugins/platforms/, plugins/image_gen/, plugins/memory/, plugins/context_engine/, plugins/model-providers/ are routed to different loaders \u2014 PluginManager vs the per-category own-loader systems. - Explicit mention of user-override semantics at ~/.hermes/plugins/model-providers/ and ~/.hermes/plugins/memory/. build-a-hermes-plugin.md: - New '## Specialized plugin types' section (5 sub-sections): - Model provider plugins \u2014 ProviderProfile + plugin.yaml example, auto-wiring summary, link to full guide - Platform plugins \u2014 BasePlatformAdapter + register_platform() skeleton - Memory provider plugins \u2014 MemoryProvider subclass example - Context engine plugins \u2014 ContextEngine subclass example - Image-generation backends \u2014 ImageGenProvider + kind: backend example - New '## Non-Python extension surfaces' section (5 sub-sections): - MCP servers \u2014 config.yaml mcp_servers.<name> example - Gateway event hooks \u2014 HOOK.yaml + handler.py example - Shell hooks \u2014 hooks: block in config.yaml example - Skill sources (taps) \u2014 hermes skills tap add example - TTS / STT command templates \u2014 tts.providers.<name> with type: command - Distribute via pip / NixOS promoted from ### to ## (they were orphaned after the reorganization) Each specialized / non-Python section has a concrete, copy-pasteable example plus a 'Full guide:' link to the authoritative doc. Devs arriving at the build-a-hermes-plugin guide now see every extension surface at their disposal, not just the general tool/hook/slash-command surface. Verified: - Docusaurus build SUCCESS, zero new broken links - All new cross-links (developer-guide/model-provider-plugin, adding-platform-adapters, memory-provider-plugin, context-engine-plugin, user-guide/features/mcp, skills#skills-hub, hooks#gateway-event-hooks, hooks#shell-hooks, tts#custom-command-providers, tts#voice-message-transcription-stt) resolve - Same 3 pre-existing broken links on main (cron-script-only, llms.txt, adding-platform-adapters#step-by-step-checklist) * docs(plugins): fix opt-in inconsistency — not every plugin is gated The 'Every plugin is disabled by default' statement was wrong. Several plugin categories intentionally bypass plugins.enabled: - Bundled platform plugins (IRC, Teams) auto-load so shipped gateway channels are available out of the box. Activation per channel is via gateway.platforms.<name>.enabled. - Bundled backends (plugins/image_gen/*) auto-load so the default backend 'just works'. Selection via <category>.provider config. - Memory providers are all discovered; one is active via memory.provider. - Context engines are all discovered; one is active via context.engine. - Model providers: all 33 discovered at first get_provider_profile(); user picks via --provider / config. The plugins.enabled allow-list specifically gates: - Standalone plugins (general tools/hooks/slash commands) - User-installed backends - User-installed platforms (third-party gateway adapters) - Pip entry-point backends Which matches the actual code in hermes_cli/plugins.py:737 where the bundled+backend/platform check bypasses the allow-list. Rewrote '## Plugins are opt-in' to: - Retitle to 'Plugins are opt-in (with a few exceptions)' - Narrow opening claim to 'General plugins and user-installed backends are disabled by default' - Added 'What the allow-list does NOT gate' subsection with a full table of which bypass the gate and how they're activated instead - Fixed migration section wording (bundled platform/backend plugins never needed grandfathering) Verified: docusaurus build SUCCESS, zero new broken links.
2026-05-06 07:24:42 -07:00
| **Model providers** | Declare an inference backend (OpenRouter, Anthropic, …) | Multi-register, picked by `--provider` / `config.yaml` | `plugins/model-providers/` |
Memory providers and context engines are **provider plugins** — only one of each type can be active at a time. Model providers are also plugins, but many load simultaneously; the user picks one at a time via `--provider` or `config.yaml`. General plugins can be enabled in any combination.
## Pluggable interfaces — where to go for each
The table above shows the four plugin categories, but within "General plugins" the `PluginContext` exposes several distinct extension points — and Hermes also accepts extensions outside the Python plugin system (config-driven backends, shell-hooked commands, external servers, etc.). Use this table to find the right doc for what you want to build:
| Want to add… | How | Authoring guide |
|---|---|---|
| A **tool** the LLM can call | Python plugin — `ctx.register_tool()` | [Build a Hermes Plugin](/docs/guides/build-a-hermes-plugin) · [Adding Tools](/docs/developer-guide/adding-tools) |
| A **lifecycle hook** (pre/post LLM, session start/end, tool filter) | Python plugin — `ctx.register_hook()` | [Hooks reference](/docs/user-guide/features/hooks) · [Build a Hermes Plugin](/docs/guides/build-a-hermes-plugin) |
| A **slash command** for the CLI / gateway | Python plugin — `ctx.register_command()` | [Build a Hermes Plugin](/docs/guides/build-a-hermes-plugin) · [Extending the CLI](/docs/developer-guide/extending-the-cli) |
| A **subcommand** for `hermes <thing>` | Python plugin — `ctx.register_cli_command()` | [Extending the CLI](/docs/developer-guide/extending-the-cli) |
| A bundled **skill** that your plugin ships | Python plugin — `ctx.register_skill()` | [Creating Skills](/docs/developer-guide/creating-skills) |
| An **inference backend** (LLM provider: OpenAI-compat, Codex, Anthropic-Messages, Bedrock) | Provider plugin — `register_provider(ProviderProfile(...))` in `plugins/model-providers/<name>/` | **[Model Provider Plugins](/docs/developer-guide/model-provider-plugin)** · [Adding Providers](/docs/developer-guide/adding-providers) |
| A **gateway channel** (Discord / Telegram / IRC / Teams / etc.) | Platform plugin — `ctx.register_platform()` in `plugins/platforms/<name>/` | [Adding Platform Adapters](/docs/developer-guide/adding-platform-adapters) |
| A **memory backend** (Honcho, Mem0, Supermemory, …) | Memory plugin — subclass `MemoryProvider` in `plugins/memory/<name>/` | [Memory Provider Plugins](/docs/developer-guide/memory-provider-plugin) |
| A **context-compression strategy** | Context-engine plugin — `ctx.register_context_engine()` | [Context Engine Plugins](/docs/developer-guide/context-engine-plugin) |
docs(plugins): close the gaps \u2014 image-gen-provider-plugin guide + publishing a skill tap (#20800) Two pluggable surfaces were mentioned in the interfaces map without a real authoring guide behind them: 1. **Image-gen backends** — only had 'See bundled examples' pointers. Now a full developer-guide/image-gen-provider-plugin.md (270 lines) mirroring the memory/context/model provider docs: - How discovery works, directory structure, plugin.yaml - ImageGenProvider ABC with every overridable method (name, display_name, is_available, list_models, default_model, get_setup_schema, generate) - Full authoring walkthrough with a working MyBackendImageGenProvider - Response-format reference (success_response / error_response) - Handling b64 vs URL output (save_b64_image helper) - User overrides at ~/.hermes/plugins/image_gen/<name>/ - Testing recipe + pip distribution - Reference examples (openai, openai-codex, xai) 2. **Skill taps** — features/skills.md mentioned the CLI commands but never explained the repo contract for publishing a tap. Added 'Publishing a custom skill tap' section under Skills Hub covering: - Repo layout (skills/<name>/SKILL.md by default) - Minimal working example - Non-default path configuration (taps.json) - Installing individual skills without subscribing - Trust-level handling - Full tap management CLI + in-session /skills tap commands Wired into: - website/sidebars.ts: image-gen-provider-plugin added to Extending group - website/docs/user-guide/features/plugins.md: pluggable interfaces table + 'What plugins can do' table now link to the real guides instead of 'See bundled examples' - website/docs/guides/build-a-hermes-plugin.md: top info map and inline sub-sections updated, 'Full guide:' line added to image-gen block, tap section mentions publishing Verified: docusaurus build SUCCESS, new page renders at /docs/developer-guide/image-gen-provider-plugin, anchor #publishing-a-custom-skill-tap resolves from plugins.md + build-a-hermes-plugin.md. Pre-existing zh-Hans broken links unchanged.
2026-05-06 08:40:05 -07:00
| An **image-generation backend** (DALL·E, SDXL, …) | Backend plugin — `ctx.register_image_gen_provider()` | [Image Generation Provider Plugins](/docs/developer-guide/image-gen-provider-plugin) |
feat(video_gen): unified video_generate tool with pluggable provider backends (#25126) * feat(video_gen): unified video_generate tool with pluggable provider backends One core video_generate tool, every backend a plugin. Mirrors the image_gen + memory_provider + context_engine architecture: ABC, registry, plugin-context registration hook, and per-plugin model catalogs surfaced through hermes tools. Surface (one schema, every backend): - operation: generate / edit / extend - modalities: text-to-video (prompt only), image-to-video (prompt + image_url), video edit (prompt + video_url), video extend (video_url) - reference_image_urls, duration, aspect_ratio, resolution, negative_prompt, audio, seed, model override - Providers ignore unknown kwargs and declare what they support via VideoGenProvider.capabilities() — backend-specific quirks stay in the backend, the agent learns one tool Backends shipped: - plugins/video_gen/xai/ — Grok-Imagine, full generate/edit/extend + image-to-video + reference images (salvaged from PR #10600 by @Jaaneek, reshaped into the plugin interface) - plugins/video_gen/fal/ — Veo 3.1 (t2v + i2v), Kling O3 i2v, Pixverse v6 i2v with model-aware payload building that drops keys a model doesn't declare Wiring: - agent/video_gen_provider.py — VideoGenProvider ABC, normalize_operation, success_response / error_response, save_b64_video / save_bytes_video, $HERMES_HOME/cache/videos/ - agent/video_gen_registry.py — thread-safe register/get/list + get_active_provider() reading video_gen.provider from config.yaml - hermes_cli/plugins.py — PluginContext.register_video_gen_provider() - hermes_cli/tools_config.py — Video Generation category in hermes tools, plugin-only providers list, model picker per plugin, config write to video_gen.{provider,model} - toolsets.py — new video_gen toolset - tests: 31 new tests covering ABC, registry, tool dispatch, both plugins - docs: developer-guide/video-gen-provider-plugin.md (parallel to the image-gen guide), sidebar + toolsets-reference + plugin guides updated Supersedes: #25035 (FAL), #17972 (FAL), #14543 (xAI), #13847 (HappyHorse), #10458 (provider categories), #10786 (xAI media+search bundle), #2984 (FAL duplicate), #19086 (Google Veo standalone — easy port to plugin interface). Co-authored-by: Jaaneek <Jaaneek@users.noreply.github.com> * feat(video_gen): dynamic schema reflects active backend's capabilities Address the 'capability variance' question — instead of one tool with a static schema that lies about what every backend supports, the video_generate tool now rebuilds its description at get_definitions() time based on the configured video_gen.provider and video_gen.model. The agent sees backend-specific guidance up-front: - 'fal-ai/veo3.1/image-to-video': 'image-to-video only — image_url is REQUIRED; text-only prompts will be rejected' - 'fal-ai/veo3.1' (t2v): no image_url restriction shown - xAI grok-imagine-video: 'operations: generate, edit, extend; up to 7 reference_image_urls' - Backends without edit/extend: 'not supported on this backend — surface that they need to switch backends via hermes tools' This is the same pattern PR #22694 used for delegate_task self-capping — documented in the dynamic-tool-schemas skill. Cache invalidation is free: get_tool_definitions() already memoizes on config.yaml mtime, so a mid-session backend swap rebuilds the schema automatically. Tested: - Empirical FAL OpenAPI schema check confirms image-to-video models require image_url (FAL returns HTTP 422 otherwise) — client-side rejection in FALVideoGenProvider.generate() now prevents the wasted round-trip - Live E2E: fal-ai/veo3.1/image-to-video + prompt-only → clean missing_image_url error; fal-ai/veo3.1 + prompt-only → dispatches - 6 new tests cover the builder (no config / image-only / full-surface / text-only / unknown provider / registry wiring), all passing - 37/37 in the slice, 134/134 in the broader regression set * test(video_gen/xai): full surface integration tests + cleaner schema Verified end-to-end that the xAI plugin handles every documented mode from PR #10600's surface: text-to-video, image-to-video, reference-images-to-video, video edit, video extend (with and without prompt). All five modes route to the correct xAI endpoint (/videos/generations, /videos/edits, /videos/extensions) with the right payload shape (image / reference_images / video keys), and all five client-side rejections fire before the network: edit-without-prompt, extend-without-video_url, image+refs conflict, >7 references, and duration/aspect_ratio clamping. 15 new integration tests grouped into four classes (endpoint routing, modalities, validation, clamping). httpx is stubbed via a small fake AsyncClient that records POSTs so the tests assert the actual payload the plugin would send to xAI — not just the success/error envelope. Also cleaned up a description redundancy: when a model's operations match the backend's overall set, we no longer print the duplicate 'operations supported by this model' line. xAI's description now reads: Active backend: xAI . model: grok-imagine-video - operations supported by this backend: edit, extend, generate - modalities supported by this backend: image, reference_images, text - aspect_ratio choices: 16:9, 1:1, 2:3, 3:2, 3:4, 4:3, 9:16 - resolution choices: 480p, 720p - duration range: 1-15s - reference_image_urls: up to 7 images Co-authored-by: Jaaneek <Jaaneek@users.noreply.github.com> * feat(video_gen): collapse surface to t2v + i2v, family-based auto-routing Two design changes per Teknium: 1) Drop edit/extend from the tool surface entirely. Only text-to-video and image-to-video remain. The agent sees a clean tool with two modalities; backend-specific quirks like xAI's edit/extend endpoints stay out of the unified schema. 2) FAL: pick a model FAMILY once, the plugin routes between the family's text-to-video and image-to-video endpoints based on whether image_url was passed. Users no longer pick 'fal-ai/veo3.1' AND 'fal-ai/veo3.1/image-to-video' as separate options — they pick 'veo3.1', and the plugin handles the rest. Catalog rewritten as families: veo3.1 fal-ai/veo3.1 / fal-ai/veo3.1/image-to-video pixverse-v6 fal-ai/pixverse/v6/text-to-video / fal-ai/pixverse/v6/image-to-video kling-o3-standard fal-ai/kling-video/o3/standard/text-to-video / fal-ai/kling-video/o3/standard/image-to-video xAI uses a single endpoint (/videos/generations) for both modes, routed by the presence of the 'image' field in the payload — no edit/extend exposure. Schema changes: - VIDEO_GENERATE_SCHEMA: drop operation, drop video_url. Final params: prompt (required), image_url, reference_image_urls, duration, aspect_ratio, resolution, negative_prompt, audio, seed, model. - VideoGenProvider ABC: drop normalize_operation, VALID_OPERATIONS, DEFAULT_OPERATION. capabilities() drops 'operations' key. - success_response: add 'modality' field ('text' | 'image') so the agent and logs can see which endpoint was actually hit. Dynamic schema builder simplified — no operations bullet, no 'switch backends if you need edit/extend' guidance. When the active backend supports both modalities (the common case), description reads: Active backend: FAL . model: pixverse-v6 - supports both text-to-video (omit image_url) and image-to-video (pass image_url) - routes automatically - aspect_ratio choices: 16:9, 9:16, 1:1 - resolution choices: 360p, 540p, 720p, 1080p - duration range: 1-15s - audio: pass audio=true to enable native audio (pricing tier) - negative_prompt: supported Tests: 51 in the video_gen slice, 216 across the broader image+video sweep, all passing. New FAL routing tests prove pixverse-v6 + no image hits text-to-video endpoint, pixverse-v6 + image_url hits image-to-video endpoint, same for veo3.1 and kling-o3-standard. Docs updated: developer-guide page rewrites the 'model families' pattern as a first-class section so external plugin authors know the convention. toolsets-reference and toolsets.py descriptions match the new surface. Co-authored-by: Jaaneek <Jaaneek@users.noreply.github.com> * feat(video_gen/fal): expand catalog to 6 families, cheap + premium tiers Catalog now covers everything Teknium specced from FAL: Cheap tier: ltx-2.3 fal-ai/ltx-2.3-22b/text-to-video / image-to-video pixverse-v6 fal-ai/pixverse/v6/text-to-video / image-to-video Premium tier: veo3.1 fal-ai/veo3.1 / fal-ai/veo3.1/image-to-video seedance-2.0 bytedance/seedance-2.0/text-to-video / image-to-video kling-v3-4k fal-ai/kling-video/v3/4k/text-to-video / image-to-video happy-horse fal-ai/happy-horse/text-to-video / image-to-video DEFAULT_MODEL moved from veo3.1 (premium) to pixverse-v6 (cheap, sane defaults, both modalities) — better first-run UX for users who haven't explicitly picked a model. New family-entry knob: image_param_key. Kling v3 4K's image-to-video endpoint expects start_image_url instead of image_url; declaring image_param_key='start_image_url' on the family lets _build_payload remap correctly. Other families default to plain image_url. Per-family capability flags reflect each model's docs: - LTX 2.3 + Happy Horse: minimal payloads (no duration/aspect/resolution enum exposed by FAL — let endpoint apply defaults) - Seedance: 6 aspect ratios incl 21:9, durations 4-15, audio supported, negative prompts NOT supported per docs - Kling v3 4K: 16:9/9:16/1:1, 3-15s, audio + negative - Veo 3.1: unchanged, 16:9/9:16, 4/6/8s Tests: +5 covering the new families (full catalog, Kling 4K start_image_url remap, Seedance routing, LTX payload minimality, Happy Horse minimality). 56/56 in the slice green. Note: I did NOT add the FAL-hosted xAI Grok-Imagine variant. Hermes already has a direct xAI plugin that talks to xAI's own API; routing the same model through FAL's wrapper would duplicate the surface without adding capabilities. Users on FAL who want Grok-Imagine should use the xAI plugin directly; flag if you want both routes available. * test(video_gen): tool-surface routing matrix — every model x modality End-to-end matrix test driven through _handle_video_generate() — the actual function the agent's video_generate tool call lands in. Writes config.yaml, invokes the registered handler with a raw args dict, then asserts the outbound HTTP/SDK call hit the right endpoint with the right payload shape. Parametrized over FAL_FAMILIES.keys() so the matrix auto-discovers new families as they're added (add a family to FAL_FAMILIES and you get both modalities tested for free). Coverage: - All 6 FAL families x {text-only, text+image} = 12 cases - xAI x {text-only, text+image} = 2 cases - tool-level model= arg overrides config = 2 cases For each case, verifies: - result['success'] is True - result['modality'] matches input shape ('text' if no image_url, 'image' otherwise) - outbound endpoint URL matches the family's text_endpoint or image_endpoint - text-only payloads carry no image-shaped keys - text+image payloads carry the family's image key (image_url for most, start_image_url for kling-v3-4k, wrapped 'image' object for xAI) All 16 cases passing. Confirms the tool surface routes every (provider, model, modality) combination correctly with zero leakage. * feat(video_gen): keep video_gen out of first-run setup, surface in status Two changes: 1. video_gen joins _DEFAULT_OFF_TOOLSETS, so it is NOT pre-selected in the first-run toolset checklist. Video gen is niche, paid, and slow — most users don't want it nagging them during initial setup. Anyone who wants it opts in via 'hermes tools' -> Video Generation, which already routes to the provider+model picker. 2. The 'hermes setup' status panel learns about video_gen — but only shows the row when a plugin reports available. Users without FAL_KEY/XAI_API_KEY see nothing about video gen; users with one of those keys see 'Video Generation (FAL) ✓' as confirmation it's wired. Verified live: - Fresh install (no creds): zero video_gen mentions in wizard. - With FAL_KEY: status row appears with active backend name. - 160/160 in the setup + tools_config + video_gen test slice. Rationale: image_gen is on by default because it's a featured creative tool used in casual chat (telegrams, etc). Video gen is heavier — long wait, paid per-second pricing. Default-off matches user intent better. --------- Co-authored-by: Jaaneek <Jaaneek@users.noreply.github.com>
2026-05-13 16:39:41 -07:00
| A **video-generation backend** (Veo, Kling, Pixverse, Grok-Imagine, Runway, …) | Backend plugin — `ctx.register_video_gen_provider()` | [Video Generation Provider Plugins](/docs/developer-guide/video-gen-provider-plugin) |
docs: pluggable surfaces coverage — model-provider guide, full plugin map, opt-in fix (#20749) * docs(providers): add model-provider-plugin authoring guide + fix stale refs New docs: - website/docs/developer-guide/model-provider-plugin.md — full authoring guide (directory layout, minimal example, ProviderProfile fields, overridable hooks, user overrides, api_mode selection, auth types, testing, pip distribution) - Wired into website/sidebars.ts under 'Extending' - Cross-references added in: - guides/build-a-hermes-plugin.md (tip block) - developer-guide/adding-providers.md - developer-guide/provider-runtime.md User guide: - user-guide/features/plugins.md: Plugin types table grows from 3 to 4 with 'Model providers' row Stale comment cleanup (providers/*.py → plugins/model-providers/<name>/): - hermes_cli/main.py:_is_profile_api_key_provider docstring - hermes_cli/doctor.py:_build_apikey_providers_list docstring - hermes_cli/auth.py: PROVIDER_REGISTRY + alias auto-extension comments - hermes_cli/models.py: CANONICAL_PROVIDERS auto-extension comment AGENTS.md: - Project-structure tree: added plugins/model-providers/ row - New section: 'Model-provider plugins' explaining discovery, override semantics, PluginManager integration, kind auto-coerce heuristic Verified: docusaurus build succeeds, new page renders, all 3 cross-links resolve. 347/347 targeted tests pass (tests/providers/, tests/hermes_cli/test_plugins.py, tests/hermes_cli/test_runtime_provider_resolution.py, tests/run_agent/test_provider_parity.py). * docs(plugins): add 'pluggable interfaces at a glance' maps to plugins.md + build-a-hermes-plugin Devs landing on either the user-guide plugin page or the build-a-plugin guide now get an upfront table of every distinct pluggable surface with a link to the right authoring doc. Previously they'd have to read the full general-plugin guide to discover that model providers / platforms / memory / context engines are separate systems. user-guide/features/plugins.md: - New 'Pluggable interfaces — where to go for each' section below the existing 4-kinds table - 10 rows covering every register_* surface (tool, hook, slash command, CLI subcommand, skill, model provider, platform, memory, context engine, image-gen) - Explicit note: TTS/STT are NOT plugin-extensible yet — documented with a pointer to the current config.yaml 'command providers' pattern and a note that register_tts_provider()/register_stt_provider() may come later guides/build-a-hermes-plugin.md: - New :::info 'Not sure which guide you need?' map at the top so devs see all pluggable interfaces before investing in this 737-line general-plugin walkthrough - Existing bottom :::tip expanded to include platform adapters alongside model/memory/context plugins Verified: - All 8 cross-doc links in the new plugins.md table resolve in a docusaurus build (SUCCESS, no new broken links) - TTS link corrected (features/voice → features/tts; latter exists) - Pre-existing broken links/anchors (cron-script-only, llms.txt, adding-platform-adapters#step-by-step-checklist) are unchanged * docs(plugins): correct TTS/STT pluggability \u2014 they ARE plugins (command-providers) Previous commit incorrectly said TTS/STT 'aren't plugin-extensible'. They are, via the config-driven command-provider pattern \u2014 any CLI that reads text and writes audio (or vice versa for STT) is automatically a plugin with zero Python. The tts.md docs cover this extensively and I missed it. plugins.md: - TTS row: 'Config-driven (not a Python plugin)', points at tts.md#custom-command-providers - STT row: points at tts.md#voice-message-transcription-stt (STT docs live in tts.md despite the filename) - Expanded note: TTS/STT use config-driven shell-command templates as their plugin surface (full tts.providers.<name> registry for TTS; HERMES_LOCAL_STT_COMMAND escape hatch for STT) - Any CLI that reads/writes files is automatically a plugin \u2014 no Python register_* API needed - Future register_tts_provider()/register_stt_provider() hooks mentioned as nice-to-have for SDK/streaming cases, not as the primary story build-a-hermes-plugin.md: - Same map update: TTS/STT rows explicit, footer note corrected Verified: - tts.md anchors (custom-command-providers, voice-message-transcription-stt) exist and resolve in docusaurus build (SUCCESS, no new broken links) * docs(plugins): expand pluggable interfaces table with MCP / event hooks / shell hooks / skill taps Broadened the scope beyond Python register_* hooks. Hermes has MULTIPLE plugin-style extension surfaces; they're now all in one table instead of being scattered across feature docs. Added rows for: - **MCP servers** — config.yaml mcp_servers.<name> auto-registers external tools from any MCP server. Huge extensibility surface, previously not linked from the plugin map. - **Gateway event hooks** — drop HOOK.yaml + handler.py into ~/.hermes/hooks/<name>/ to fire on gateway:startup, session:*, agent:*, command:* events. Separate from Python plugin hooks. - **Shell hooks** — hooks: block in config.yaml runs shell commands on events (notifications, auditing, etc.). - **Skill sources (taps)** — hermes skills tap add <repo> to pull in new skill registries beyond the built-in sources. Both docs updated: - user-guide/features/plugins.md: table column renamed to 'How' (mixes Python API + config-driven + drop-in-dir surfaces accurately) - guides/build-a-hermes-plugin.md: :::info map at top mirrors the new surfaces with a forward-link to the consolidated table Note block rewritten: instead of singling out TTS/STT as the 'different style' exception, now honestly describes that Hermes deliberately supports three plugin styles — Python APIs, config-driven commands, and drop-in manifest directories — and devs should pick the one that fits their integration. Not included (considered and rejected): - Transport layer (register_transport) — internal, not user-facing - Tool-call parsers — internal, VLLM phase-2 thing - Cloud browser providers — hardcoded registry, not drop-in yet - Terminal backends — hardcoded if/elif, not drop-in yet - Skill sources (the ABC) — hardcoded list, only taps are user-extensible Verified: - All 5 new anchors resolve (gateway-event-hooks, shell-hooks, skills-hub, custom-command-providers, voice-message-transcription-stt) - Docusaurus build SUCCESS, zero new broken links - Same 3 pre-existing broken links on main (cron-script-only, llms.txt, adding-platform-adapters#step-by-step-checklist) * docs(plugins): cover every pluggable surface in both the overview and how-to Both plugins.md and build-a-hermes-plugin.md now cover every extension surface end-to-end \u2014 general plugin APIs, specialized plugin types, config-driven surfaces \u2014 with concrete authoring patterns for each. plugins.md: - 'What plugins can do' table grows from 9 rows (general ctx.register_* only) to 14 rows covering register_platform, register_image_gen_provider, register_context_engine, MemoryProvider subclass, register_provider (model). Each row links to its full authoring guide. - New 'Plugin sub-categories' section under Plugin Discovery explains how plugins/platforms/, plugins/image_gen/, plugins/memory/, plugins/context_engine/, plugins/model-providers/ are routed to different loaders \u2014 PluginManager vs the per-category own-loader systems. - Explicit mention of user-override semantics at ~/.hermes/plugins/model-providers/ and ~/.hermes/plugins/memory/. build-a-hermes-plugin.md: - New '## Specialized plugin types' section (5 sub-sections): - Model provider plugins \u2014 ProviderProfile + plugin.yaml example, auto-wiring summary, link to full guide - Platform plugins \u2014 BasePlatformAdapter + register_platform() skeleton - Memory provider plugins \u2014 MemoryProvider subclass example - Context engine plugins \u2014 ContextEngine subclass example - Image-generation backends \u2014 ImageGenProvider + kind: backend example - New '## Non-Python extension surfaces' section (5 sub-sections): - MCP servers \u2014 config.yaml mcp_servers.<name> example - Gateway event hooks \u2014 HOOK.yaml + handler.py example - Shell hooks \u2014 hooks: block in config.yaml example - Skill sources (taps) \u2014 hermes skills tap add example - TTS / STT command templates \u2014 tts.providers.<name> with type: command - Distribute via pip / NixOS promoted from ### to ## (they were orphaned after the reorganization) Each specialized / non-Python section has a concrete, copy-pasteable example plus a 'Full guide:' link to the authoritative doc. Devs arriving at the build-a-hermes-plugin guide now see every extension surface at their disposal, not just the general tool/hook/slash-command surface. Verified: - Docusaurus build SUCCESS, zero new broken links - All new cross-links (developer-guide/model-provider-plugin, adding-platform-adapters, memory-provider-plugin, context-engine-plugin, user-guide/features/mcp, skills#skills-hub, hooks#gateway-event-hooks, hooks#shell-hooks, tts#custom-command-providers, tts#voice-message-transcription-stt) resolve - Same 3 pre-existing broken links on main (cron-script-only, llms.txt, adding-platform-adapters#step-by-step-checklist) * docs(plugins): fix opt-in inconsistency — not every plugin is gated The 'Every plugin is disabled by default' statement was wrong. Several plugin categories intentionally bypass plugins.enabled: - Bundled platform plugins (IRC, Teams) auto-load so shipped gateway channels are available out of the box. Activation per channel is via gateway.platforms.<name>.enabled. - Bundled backends (plugins/image_gen/*) auto-load so the default backend 'just works'. Selection via <category>.provider config. - Memory providers are all discovered; one is active via memory.provider. - Context engines are all discovered; one is active via context.engine. - Model providers: all 33 discovered at first get_provider_profile(); user picks via --provider / config. The plugins.enabled allow-list specifically gates: - Standalone plugins (general tools/hooks/slash commands) - User-installed backends - User-installed platforms (third-party gateway adapters) - Pip entry-point backends Which matches the actual code in hermes_cli/plugins.py:737 where the bundled+backend/platform check bypasses the allow-list. Rewrote '## Plugins are opt-in' to: - Retitle to 'Plugins are opt-in (with a few exceptions)' - Narrow opening claim to 'General plugins and user-installed backends are disabled by default' - Added 'What the allow-list does NOT gate' subsection with a full table of which bypass the gate and how they're activated instead - Fixed migration section wording (bundled platform/backend plugins never needed grandfathering) Verified: docusaurus build SUCCESS, zero new broken links.
2026-05-06 07:24:42 -07:00
| A **TTS backend** (any CLI — Piper, VoxCPM, Kokoro, xtts, voice-cloning scripts, …) | Config-driven — declare under `tts.providers.<name>` with `type: command` in `config.yaml` | [TTS setup](/docs/user-guide/features/tts#custom-command-providers) |
| An **STT backend** (custom whisper binary, local ASR CLI) | Config-driven — set `HERMES_LOCAL_STT_COMMAND` env var to a shell template | [Voice Message Transcription (STT)](/docs/user-guide/features/tts#voice-message-transcription-stt) |
| **External tools via MCP** (filesystem, GitHub, Linear, Notion, any MCP server) | Config-driven — declare `mcp_servers.<name>` with `command:` / `url:` in `config.yaml`. Hermes auto-discovers the server's tools and registers them alongside built-ins. | [MCP](/docs/user-guide/features/mcp) |
docs(plugins): close the gaps \u2014 image-gen-provider-plugin guide + publishing a skill tap (#20800) Two pluggable surfaces were mentioned in the interfaces map without a real authoring guide behind them: 1. **Image-gen backends** — only had 'See bundled examples' pointers. Now a full developer-guide/image-gen-provider-plugin.md (270 lines) mirroring the memory/context/model provider docs: - How discovery works, directory structure, plugin.yaml - ImageGenProvider ABC with every overridable method (name, display_name, is_available, list_models, default_model, get_setup_schema, generate) - Full authoring walkthrough with a working MyBackendImageGenProvider - Response-format reference (success_response / error_response) - Handling b64 vs URL output (save_b64_image helper) - User overrides at ~/.hermes/plugins/image_gen/<name>/ - Testing recipe + pip distribution - Reference examples (openai, openai-codex, xai) 2. **Skill taps** — features/skills.md mentioned the CLI commands but never explained the repo contract for publishing a tap. Added 'Publishing a custom skill tap' section under Skills Hub covering: - Repo layout (skills/<name>/SKILL.md by default) - Minimal working example - Non-default path configuration (taps.json) - Installing individual skills without subscribing - Trust-level handling - Full tap management CLI + in-session /skills tap commands Wired into: - website/sidebars.ts: image-gen-provider-plugin added to Extending group - website/docs/user-guide/features/plugins.md: pluggable interfaces table + 'What plugins can do' table now link to the real guides instead of 'See bundled examples' - website/docs/guides/build-a-hermes-plugin.md: top info map and inline sub-sections updated, 'Full guide:' line added to image-gen block, tap section mentions publishing Verified: docusaurus build SUCCESS, new page renders at /docs/developer-guide/image-gen-provider-plugin, anchor #publishing-a-custom-skill-tap resolves from plugins.md + build-a-hermes-plugin.md. Pre-existing zh-Hans broken links unchanged.
2026-05-06 08:40:05 -07:00
| **Additional skill sources** (custom GitHub repos, private skill indexes) | CLI — `hermes skills tap add <repo>` | [Skills Hub](/docs/user-guide/features/skills#skills-hub) · [Publishing a custom tap](/docs/user-guide/features/skills#publishing-a-custom-skill-tap) |
docs: pluggable surfaces coverage — model-provider guide, full plugin map, opt-in fix (#20749) * docs(providers): add model-provider-plugin authoring guide + fix stale refs New docs: - website/docs/developer-guide/model-provider-plugin.md — full authoring guide (directory layout, minimal example, ProviderProfile fields, overridable hooks, user overrides, api_mode selection, auth types, testing, pip distribution) - Wired into website/sidebars.ts under 'Extending' - Cross-references added in: - guides/build-a-hermes-plugin.md (tip block) - developer-guide/adding-providers.md - developer-guide/provider-runtime.md User guide: - user-guide/features/plugins.md: Plugin types table grows from 3 to 4 with 'Model providers' row Stale comment cleanup (providers/*.py → plugins/model-providers/<name>/): - hermes_cli/main.py:_is_profile_api_key_provider docstring - hermes_cli/doctor.py:_build_apikey_providers_list docstring - hermes_cli/auth.py: PROVIDER_REGISTRY + alias auto-extension comments - hermes_cli/models.py: CANONICAL_PROVIDERS auto-extension comment AGENTS.md: - Project-structure tree: added plugins/model-providers/ row - New section: 'Model-provider plugins' explaining discovery, override semantics, PluginManager integration, kind auto-coerce heuristic Verified: docusaurus build succeeds, new page renders, all 3 cross-links resolve. 347/347 targeted tests pass (tests/providers/, tests/hermes_cli/test_plugins.py, tests/hermes_cli/test_runtime_provider_resolution.py, tests/run_agent/test_provider_parity.py). * docs(plugins): add 'pluggable interfaces at a glance' maps to plugins.md + build-a-hermes-plugin Devs landing on either the user-guide plugin page or the build-a-plugin guide now get an upfront table of every distinct pluggable surface with a link to the right authoring doc. Previously they'd have to read the full general-plugin guide to discover that model providers / platforms / memory / context engines are separate systems. user-guide/features/plugins.md: - New 'Pluggable interfaces — where to go for each' section below the existing 4-kinds table - 10 rows covering every register_* surface (tool, hook, slash command, CLI subcommand, skill, model provider, platform, memory, context engine, image-gen) - Explicit note: TTS/STT are NOT plugin-extensible yet — documented with a pointer to the current config.yaml 'command providers' pattern and a note that register_tts_provider()/register_stt_provider() may come later guides/build-a-hermes-plugin.md: - New :::info 'Not sure which guide you need?' map at the top so devs see all pluggable interfaces before investing in this 737-line general-plugin walkthrough - Existing bottom :::tip expanded to include platform adapters alongside model/memory/context plugins Verified: - All 8 cross-doc links in the new plugins.md table resolve in a docusaurus build (SUCCESS, no new broken links) - TTS link corrected (features/voice → features/tts; latter exists) - Pre-existing broken links/anchors (cron-script-only, llms.txt, adding-platform-adapters#step-by-step-checklist) are unchanged * docs(plugins): correct TTS/STT pluggability \u2014 they ARE plugins (command-providers) Previous commit incorrectly said TTS/STT 'aren't plugin-extensible'. They are, via the config-driven command-provider pattern \u2014 any CLI that reads text and writes audio (or vice versa for STT) is automatically a plugin with zero Python. The tts.md docs cover this extensively and I missed it. plugins.md: - TTS row: 'Config-driven (not a Python plugin)', points at tts.md#custom-command-providers - STT row: points at tts.md#voice-message-transcription-stt (STT docs live in tts.md despite the filename) - Expanded note: TTS/STT use config-driven shell-command templates as their plugin surface (full tts.providers.<name> registry for TTS; HERMES_LOCAL_STT_COMMAND escape hatch for STT) - Any CLI that reads/writes files is automatically a plugin \u2014 no Python register_* API needed - Future register_tts_provider()/register_stt_provider() hooks mentioned as nice-to-have for SDK/streaming cases, not as the primary story build-a-hermes-plugin.md: - Same map update: TTS/STT rows explicit, footer note corrected Verified: - tts.md anchors (custom-command-providers, voice-message-transcription-stt) exist and resolve in docusaurus build (SUCCESS, no new broken links) * docs(plugins): expand pluggable interfaces table with MCP / event hooks / shell hooks / skill taps Broadened the scope beyond Python register_* hooks. Hermes has MULTIPLE plugin-style extension surfaces; they're now all in one table instead of being scattered across feature docs. Added rows for: - **MCP servers** — config.yaml mcp_servers.<name> auto-registers external tools from any MCP server. Huge extensibility surface, previously not linked from the plugin map. - **Gateway event hooks** — drop HOOK.yaml + handler.py into ~/.hermes/hooks/<name>/ to fire on gateway:startup, session:*, agent:*, command:* events. Separate from Python plugin hooks. - **Shell hooks** — hooks: block in config.yaml runs shell commands on events (notifications, auditing, etc.). - **Skill sources (taps)** — hermes skills tap add <repo> to pull in new skill registries beyond the built-in sources. Both docs updated: - user-guide/features/plugins.md: table column renamed to 'How' (mixes Python API + config-driven + drop-in-dir surfaces accurately) - guides/build-a-hermes-plugin.md: :::info map at top mirrors the new surfaces with a forward-link to the consolidated table Note block rewritten: instead of singling out TTS/STT as the 'different style' exception, now honestly describes that Hermes deliberately supports three plugin styles — Python APIs, config-driven commands, and drop-in manifest directories — and devs should pick the one that fits their integration. Not included (considered and rejected): - Transport layer (register_transport) — internal, not user-facing - Tool-call parsers — internal, VLLM phase-2 thing - Cloud browser providers — hardcoded registry, not drop-in yet - Terminal backends — hardcoded if/elif, not drop-in yet - Skill sources (the ABC) — hardcoded list, only taps are user-extensible Verified: - All 5 new anchors resolve (gateway-event-hooks, shell-hooks, skills-hub, custom-command-providers, voice-message-transcription-stt) - Docusaurus build SUCCESS, zero new broken links - Same 3 pre-existing broken links on main (cron-script-only, llms.txt, adding-platform-adapters#step-by-step-checklist) * docs(plugins): cover every pluggable surface in both the overview and how-to Both plugins.md and build-a-hermes-plugin.md now cover every extension surface end-to-end \u2014 general plugin APIs, specialized plugin types, config-driven surfaces \u2014 with concrete authoring patterns for each. plugins.md: - 'What plugins can do' table grows from 9 rows (general ctx.register_* only) to 14 rows covering register_platform, register_image_gen_provider, register_context_engine, MemoryProvider subclass, register_provider (model). Each row links to its full authoring guide. - New 'Plugin sub-categories' section under Plugin Discovery explains how plugins/platforms/, plugins/image_gen/, plugins/memory/, plugins/context_engine/, plugins/model-providers/ are routed to different loaders \u2014 PluginManager vs the per-category own-loader systems. - Explicit mention of user-override semantics at ~/.hermes/plugins/model-providers/ and ~/.hermes/plugins/memory/. build-a-hermes-plugin.md: - New '## Specialized plugin types' section (5 sub-sections): - Model provider plugins \u2014 ProviderProfile + plugin.yaml example, auto-wiring summary, link to full guide - Platform plugins \u2014 BasePlatformAdapter + register_platform() skeleton - Memory provider plugins \u2014 MemoryProvider subclass example - Context engine plugins \u2014 ContextEngine subclass example - Image-generation backends \u2014 ImageGenProvider + kind: backend example - New '## Non-Python extension surfaces' section (5 sub-sections): - MCP servers \u2014 config.yaml mcp_servers.<name> example - Gateway event hooks \u2014 HOOK.yaml + handler.py example - Shell hooks \u2014 hooks: block in config.yaml example - Skill sources (taps) \u2014 hermes skills tap add example - TTS / STT command templates \u2014 tts.providers.<name> with type: command - Distribute via pip / NixOS promoted from ### to ## (they were orphaned after the reorganization) Each specialized / non-Python section has a concrete, copy-pasteable example plus a 'Full guide:' link to the authoritative doc. Devs arriving at the build-a-hermes-plugin guide now see every extension surface at their disposal, not just the general tool/hook/slash-command surface. Verified: - Docusaurus build SUCCESS, zero new broken links - All new cross-links (developer-guide/model-provider-plugin, adding-platform-adapters, memory-provider-plugin, context-engine-plugin, user-guide/features/mcp, skills#skills-hub, hooks#gateway-event-hooks, hooks#shell-hooks, tts#custom-command-providers, tts#voice-message-transcription-stt) resolve - Same 3 pre-existing broken links on main (cron-script-only, llms.txt, adding-platform-adapters#step-by-step-checklist) * docs(plugins): fix opt-in inconsistency — not every plugin is gated The 'Every plugin is disabled by default' statement was wrong. Several plugin categories intentionally bypass plugins.enabled: - Bundled platform plugins (IRC, Teams) auto-load so shipped gateway channels are available out of the box. Activation per channel is via gateway.platforms.<name>.enabled. - Bundled backends (plugins/image_gen/*) auto-load so the default backend 'just works'. Selection via <category>.provider config. - Memory providers are all discovered; one is active via memory.provider. - Context engines are all discovered; one is active via context.engine. - Model providers: all 33 discovered at first get_provider_profile(); user picks via --provider / config. The plugins.enabled allow-list specifically gates: - Standalone plugins (general tools/hooks/slash commands) - User-installed backends - User-installed platforms (third-party gateway adapters) - Pip entry-point backends Which matches the actual code in hermes_cli/plugins.py:737 where the bundled+backend/platform check bypasses the allow-list. Rewrote '## Plugins are opt-in' to: - Retitle to 'Plugins are opt-in (with a few exceptions)' - Narrow opening claim to 'General plugins and user-installed backends are disabled by default' - Added 'What the allow-list does NOT gate' subsection with a full table of which bypass the gate and how they're activated instead - Fixed migration section wording (bundled platform/backend plugins never needed grandfathering) Verified: docusaurus build SUCCESS, zero new broken links.
2026-05-06 07:24:42 -07:00
| **Gateway event hooks** (fire on `gateway:startup`, `session:start`, `agent:end`, `command:*`) | Drop `HOOK.yaml` + `handler.py` into `~/.hermes/hooks/<name>/` | [Event Hooks](/docs/user-guide/features/hooks#gateway-event-hooks) |
| **Shell hooks** (run a shell command on events — notifications, audit logs, desktop alerts) | Config-driven — declare under `hooks:` in `config.yaml` | [Shell Hooks](/docs/user-guide/features/hooks#shell-hooks) |
:::note
Not everything is a Python plugin. Some extension surfaces intentionally use **config-driven shell commands** (TTS, STT, shell hooks) so any CLI you already have becomes a plugin without writing Python. Others are **external servers** (MCP) the agent connects to and auto-registers tools from. And some are **drop-in directories** (gateway hooks) with their own manifest format. Pick the right surface for the integration style that fits your use case; the authoring guides in the table above each cover placeholders, discovery, and examples.
:::
feat(nix): declarative plugin installation for NixOS module (#15953) * feat(nix): parameterize dependency-groups in python.nix * refactor(nix): extract package to callPackage-able hermes-agent.nix Makes the package overridable via .override{} and adds extraPythonPackages parameter for PYTHONPATH injection. Includes build-time collision check using PEP 503 name canonicalization. * feat(nix): add overlay for external NixOS consumption External flakes can now add overlays = [ inputs.hermes-agent.overlays.default ] to get pkgs.hermes-agent with full .override support. * test(nix): add check for extraPythonPackages PYTHONPATH injection Verifies wrapper has PYTHONPATH when extras provided, and base package has no PYTHONPATH without extras. * feat(nix): add extraPlugins option for directory-based plugins Symlinks plugin packages into HERMES_HOME/plugins/ at activation time. Validates plugin.yaml presence. Asserts unique plugin names at eval time. Hermes discovers them automatically via its directory scan. * feat(nix): add extraPythonPackages option for entry-point plugins Overrides the hermes package with PYTHONPATH injection when extraPythonPackages is non-empty. Plugin .dist-info directories become visible to importlib.metadata for entry-point discovery. Works in both native systemd and container modes. * docs: add NixOS declarative plugin installation to nix-setup, plugins, and build-a-plugin guides - nix-setup.md: new Plugins section with extraPlugins/extraPythonPackages examples, overlay usage, collision checking note, options reference rows - plugins.md: Nix row in discovery table, NixOS declarative plugins section - build-a-hermes-plugin.md: Distribute for NixOS section after pip section * fix: address review feedback — remove unrelated umask, fix fetchFromGitHub naming, simplify checks - Remove accidentally introduced umask/migration changes (unrelated to plugins) - Add pluginName helper, fix fetchFromGitHub producing name='source' - Show name= in extraPlugins example docs - Simplify checks.nix: use hermes-agent.override instead of re-callPackage - Fix fragile grep shell logic in checks * refactor: address simplify feedback — lib.getName, drop unused inputs', Python list for extras - Use lib.getName instead of custom pluginName helper - Drop unused inputs' from checks.nix perSystem args - Pass extraPythonPackages as Python list literal instead of colon-split string * fix: walk propagatedBuildInputs for plugin PYTHONPATH and collision check Uses python312.pkgs.requiredPythonModules to resolve the full transitive closure of extraPythonPackages. Without this, a plugin with third-party deps (e.g. requests) would fail at runtime if those deps weren't already in the sealed uv2nix venv. The collision check now also scans the full closure, catching transitive conflicts. * cleanup: fold plugins into subdir loop, use find for symlink cleanup, inline lib.getName - Add 'plugins' to the existing cron/sessions/logs/memories subdir loop instead of a separate mkdir/chown/chmod block - Replace fragile for-glob with find -delete for stale symlink cleanup - Inline lib.getName at both call sites, remove pluginName wrapper
2026-04-28 00:18:32 +05:30
## NixOS declarative plugins
On NixOS, plugins can be installed declaratively via the module options — no `hermes plugins install` needed. See the **[Nix Setup guide](/docs/getting-started/nix-setup#plugins)** for full details.
```nix
services.hermes-agent = {
# Directory plugin (source tree with plugin.yaml)
extraPlugins = [ (pkgs.fetchFromGitHub { ... }) ];
# Entry-point plugin (pip package)
extraPythonPackages = [ (pkgs.python312Packages.buildPythonPackage { ... }) ];
# Enable in config
settings.plugins.enabled = [ "my-plugin" ];
};
```
Declarative plugins are symlinked with a `nix-managed-` prefix — they coexist with manually installed plugins and are cleaned up automatically when removed from the Nix config.
## Managing plugins
```bash
fix(plugins): surface category-namespaced plugins in hermes plugins list `_discover_all_plugins()` in plugins_cmd.py did a flat scan of the bundled and user plugin directories — only direct children with a plugin.yaml were surfaced. Category directories like `observability/`, `image_gen/`, `platforms/`, `model-providers/`, `web/`, and `video_gen/` have no plugin.yaml of their own, so their nested plugins (`observability/langfuse`, `image_gen/openai`, etc.) never appeared in `hermes plugins list` or the interactive `hermes plugins` UI — even though the runtime loader (`PluginManager._scan_directory_level`) discovers them correctly and they do load at runtime. This broke the documented promise that bundled plugins appear in `hermes plugins list` and the interactive UI before being enabled, and made it look like `observability/langfuse` didn't exist. Refactor `_discover_all_plugins()` to mirror the loader's recursion (depth cap = 2, same skip set, user overrides bundled on key collision). Return the path-derived registry key (e.g. `observability/langfuse`) as the displayed name, matching what the user passes to `hermes plugins enable …` / writes under `plugins.enabled` in config.yaml. Also clarify the plugins docs: spell out that sub-category plugins surface by their `<category>/<plugin>` key in `hermes plugins list` / interactive UI, add an `observability/langfuse` example to the command reference, and include a nested entry in the interactive-UI mock. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-16 22:55:28 +00:00
hermes plugins # unified interactive UI
hermes plugins list # table: enabled / disabled / not enabled
hermes plugins install user/repo # install from Git, then prompt Enable? [y/N]
hermes plugins install user/repo --enable # install AND enable (no prompt)
hermes plugins install user/repo --no-enable # install but leave disabled (no prompt)
hermes plugins update my-plugin # pull latest
hermes plugins remove my-plugin # uninstall
hermes plugins enable my-plugin # add to allow-list (flat plugin)
hermes plugins enable observability/langfuse # add to allow-list (sub-category plugin)
hermes plugins disable my-plugin # remove from allow-list + add to disabled
```
fix(plugins): surface category-namespaced plugins in hermes plugins list `_discover_all_plugins()` in plugins_cmd.py did a flat scan of the bundled and user plugin directories — only direct children with a plugin.yaml were surfaced. Category directories like `observability/`, `image_gen/`, `platforms/`, `model-providers/`, `web/`, and `video_gen/` have no plugin.yaml of their own, so their nested plugins (`observability/langfuse`, `image_gen/openai`, etc.) never appeared in `hermes plugins list` or the interactive `hermes plugins` UI — even though the runtime loader (`PluginManager._scan_directory_level`) discovers them correctly and they do load at runtime. This broke the documented promise that bundled plugins appear in `hermes plugins list` and the interactive UI before being enabled, and made it look like `observability/langfuse` didn't exist. Refactor `_discover_all_plugins()` to mirror the loader's recursion (depth cap = 2, same skip set, user overrides bundled on key collision). Return the path-derived registry key (e.g. `observability/langfuse`) as the displayed name, matching what the user passes to `hermes plugins enable …` / writes under `plugins.enabled` in config.yaml. Also clarify the plugins docs: spell out that sub-category plugins surface by their `<category>/<plugin>` key in `hermes plugins list` / interactive UI, add an `observability/langfuse` example to the command reference, and include a nested entry in the interactive-UI mock. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-16 22:55:28 +00:00
For plugins under a sub-category directory (e.g. `plugins/observability/langfuse/`, `plugins/image_gen/openai/`), use the full `<category>/<plugin>` key — that's exactly what `hermes plugins list` shows in the **Name** column.
### Interactive UI
Running `hermes plugins` with no arguments opens a composite interactive screen:
```
Plugins
↑↓ navigate SPACE toggle ENTER configure/confirm ESC done
General Plugins
→ [✓] my-tool-plugin — Custom search tool
[ ] webhook-notifier — Event hooks
feat(plugins): make all plugins opt-in by default Plugins now require explicit consent to load. Discovery still finds every plugin — user-installed, bundled, and pip — so they all show up in `hermes plugins` and `/plugins`, but the loader only instantiates plugins whose name appears in `plugins.enabled` in config.yaml. This removes the previous ambient-execution risk where a newly-installed or bundled plugin could register hooks, tools, and commands on first run without the user opting in. The three-state model is now explicit: enabled — in plugins.enabled, loads on next session disabled — in plugins.disabled, never loads (wins over enabled) not enabled — discovered but never opted in (default for new installs) `hermes plugins install <repo>` prompts "Enable 'name' now? [y/N]" (defaults to no). New `--enable` / `--no-enable` flags skip the prompt for scripted installs. `hermes plugins enable/disable` manage both lists so a disabled plugin stays explicitly off even if something later adds it to enabled. Config migration (schema v20 → v21): existing user plugins already installed under ~/.hermes/plugins/ (minus anything in plugins.disabled) are auto-grandfathered into plugins.enabled so upgrades don't silently break working setups. Bundled plugins are NOT grandfathered — even existing users have to opt in explicitly. Also: HERMES_DISABLE_BUNDLED_PLUGINS env var removed (redundant with opt-in default), cmd_list now shows bundled + user plugins together with their three-state status, interactive UI tags bundled entries [bundled], docs updated across plugins.md and built-in-plugins.md. Validation: 442 plugin/config tests pass. E2E: fresh install discovers disk-cleanup but does not load it; `hermes plugins enable disk-cleanup` activates hooks; migration grandfathers existing user plugins correctly while leaving bundled plugins off.
2026-04-20 04:40:17 -07:00
[ ] disk-cleanup — Auto-cleanup of ephemeral files [bundled]
fix(plugins): surface category-namespaced plugins in hermes plugins list `_discover_all_plugins()` in plugins_cmd.py did a flat scan of the bundled and user plugin directories — only direct children with a plugin.yaml were surfaced. Category directories like `observability/`, `image_gen/`, `platforms/`, `model-providers/`, `web/`, and `video_gen/` have no plugin.yaml of their own, so their nested plugins (`observability/langfuse`, `image_gen/openai`, etc.) never appeared in `hermes plugins list` or the interactive `hermes plugins` UI — even though the runtime loader (`PluginManager._scan_directory_level`) discovers them correctly and they do load at runtime. This broke the documented promise that bundled plugins appear in `hermes plugins list` and the interactive UI before being enabled, and made it look like `observability/langfuse` didn't exist. Refactor `_discover_all_plugins()` to mirror the loader's recursion (depth cap = 2, same skip set, user overrides bundled on key collision). Return the path-derived registry key (e.g. `observability/langfuse`) as the displayed name, matching what the user passes to `hermes plugins enable …` / writes under `plugins.enabled` in config.yaml. Also clarify the plugins docs: spell out that sub-category plugins surface by their `<category>/<plugin>` key in `hermes plugins list` / interactive UI, add an `observability/langfuse` example to the command reference, and include a nested entry in the interactive-UI mock. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-16 22:55:28 +00:00
[ ] observability/langfuse — Trace turns / LLM calls / tools to Langfuse [bundled]
Provider Plugins
Memory Provider ▸ honcho
Context Engine ▸ compressor
```
feat(plugins): make all plugins opt-in by default Plugins now require explicit consent to load. Discovery still finds every plugin — user-installed, bundled, and pip — so they all show up in `hermes plugins` and `/plugins`, but the loader only instantiates plugins whose name appears in `plugins.enabled` in config.yaml. This removes the previous ambient-execution risk where a newly-installed or bundled plugin could register hooks, tools, and commands on first run without the user opting in. The three-state model is now explicit: enabled — in plugins.enabled, loads on next session disabled — in plugins.disabled, never loads (wins over enabled) not enabled — discovered but never opted in (default for new installs) `hermes plugins install <repo>` prompts "Enable 'name' now? [y/N]" (defaults to no). New `--enable` / `--no-enable` flags skip the prompt for scripted installs. `hermes plugins enable/disable` manage both lists so a disabled plugin stays explicitly off even if something later adds it to enabled. Config migration (schema v20 → v21): existing user plugins already installed under ~/.hermes/plugins/ (minus anything in plugins.disabled) are auto-grandfathered into plugins.enabled so upgrades don't silently break working setups. Bundled plugins are NOT grandfathered — even existing users have to opt in explicitly. Also: HERMES_DISABLE_BUNDLED_PLUGINS env var removed (redundant with opt-in default), cmd_list now shows bundled + user plugins together with their three-state status, interactive UI tags bundled entries [bundled], docs updated across plugins.md and built-in-plugins.md. Validation: 442 plugin/config tests pass. E2E: fresh install discovers disk-cleanup but does not load it; `hermes plugins enable disk-cleanup` activates hooks; migration grandfathers existing user plugins correctly while leaving bundled plugins off.
2026-04-20 04:40:17 -07:00
- **General Plugins section** — checkboxes, toggle with SPACE. Checked = in `plugins.enabled`, unchecked = in `plugins.disabled` (explicit off).
- **Provider Plugins section** — shows current selection. Press ENTER to drill into a radio picker where you choose one active provider.
feat(plugins): make all plugins opt-in by default Plugins now require explicit consent to load. Discovery still finds every plugin — user-installed, bundled, and pip — so they all show up in `hermes plugins` and `/plugins`, but the loader only instantiates plugins whose name appears in `plugins.enabled` in config.yaml. This removes the previous ambient-execution risk where a newly-installed or bundled plugin could register hooks, tools, and commands on first run without the user opting in. The three-state model is now explicit: enabled — in plugins.enabled, loads on next session disabled — in plugins.disabled, never loads (wins over enabled) not enabled — discovered but never opted in (default for new installs) `hermes plugins install <repo>` prompts "Enable 'name' now? [y/N]" (defaults to no). New `--enable` / `--no-enable` flags skip the prompt for scripted installs. `hermes plugins enable/disable` manage both lists so a disabled plugin stays explicitly off even if something later adds it to enabled. Config migration (schema v20 → v21): existing user plugins already installed under ~/.hermes/plugins/ (minus anything in plugins.disabled) are auto-grandfathered into plugins.enabled so upgrades don't silently break working setups. Bundled plugins are NOT grandfathered — even existing users have to opt in explicitly. Also: HERMES_DISABLE_BUNDLED_PLUGINS env var removed (redundant with opt-in default), cmd_list now shows bundled + user plugins together with their three-state status, interactive UI tags bundled entries [bundled], docs updated across plugins.md and built-in-plugins.md. Validation: 442 plugin/config tests pass. E2E: fresh install discovers disk-cleanup but does not load it; `hermes plugins enable disk-cleanup` activates hooks; migration grandfathers existing user plugins correctly while leaving bundled plugins off.
2026-04-20 04:40:17 -07:00
- Bundled plugins appear in the same list with a `[bundled]` tag.
Provider plugin selections are saved to `config.yaml`:
```yaml
memory:
provider: "honcho" # empty string = built-in only
context:
engine: "compressor" # default built-in compressor
```
feat(plugins): make all plugins opt-in by default Plugins now require explicit consent to load. Discovery still finds every plugin — user-installed, bundled, and pip — so they all show up in `hermes plugins` and `/plugins`, but the loader only instantiates plugins whose name appears in `plugins.enabled` in config.yaml. This removes the previous ambient-execution risk where a newly-installed or bundled plugin could register hooks, tools, and commands on first run without the user opting in. The three-state model is now explicit: enabled — in plugins.enabled, loads on next session disabled — in plugins.disabled, never loads (wins over enabled) not enabled — discovered but never opted in (default for new installs) `hermes plugins install <repo>` prompts "Enable 'name' now? [y/N]" (defaults to no). New `--enable` / `--no-enable` flags skip the prompt for scripted installs. `hermes plugins enable/disable` manage both lists so a disabled plugin stays explicitly off even if something later adds it to enabled. Config migration (schema v20 → v21): existing user plugins already installed under ~/.hermes/plugins/ (minus anything in plugins.disabled) are auto-grandfathered into plugins.enabled so upgrades don't silently break working setups. Bundled plugins are NOT grandfathered — even existing users have to opt in explicitly. Also: HERMES_DISABLE_BUNDLED_PLUGINS env var removed (redundant with opt-in default), cmd_list now shows bundled + user plugins together with their three-state status, interactive UI tags bundled entries [bundled], docs updated across plugins.md and built-in-plugins.md. Validation: 442 plugin/config tests pass. E2E: fresh install discovers disk-cleanup but does not load it; `hermes plugins enable disk-cleanup` activates hooks; migration grandfathers existing user plugins correctly while leaving bundled plugins off.
2026-04-20 04:40:17 -07:00
### Enabled vs. disabled vs. neither
feat(plugins): make all plugins opt-in by default Plugins now require explicit consent to load. Discovery still finds every plugin — user-installed, bundled, and pip — so they all show up in `hermes plugins` and `/plugins`, but the loader only instantiates plugins whose name appears in `plugins.enabled` in config.yaml. This removes the previous ambient-execution risk where a newly-installed or bundled plugin could register hooks, tools, and commands on first run without the user opting in. The three-state model is now explicit: enabled — in plugins.enabled, loads on next session disabled — in plugins.disabled, never loads (wins over enabled) not enabled — discovered but never opted in (default for new installs) `hermes plugins install <repo>` prompts "Enable 'name' now? [y/N]" (defaults to no). New `--enable` / `--no-enable` flags skip the prompt for scripted installs. `hermes plugins enable/disable` manage both lists so a disabled plugin stays explicitly off even if something later adds it to enabled. Config migration (schema v20 → v21): existing user plugins already installed under ~/.hermes/plugins/ (minus anything in plugins.disabled) are auto-grandfathered into plugins.enabled so upgrades don't silently break working setups. Bundled plugins are NOT grandfathered — even existing users have to opt in explicitly. Also: HERMES_DISABLE_BUNDLED_PLUGINS env var removed (redundant with opt-in default), cmd_list now shows bundled + user plugins together with their three-state status, interactive UI tags bundled entries [bundled], docs updated across plugins.md and built-in-plugins.md. Validation: 442 plugin/config tests pass. E2E: fresh install discovers disk-cleanup but does not load it; `hermes plugins enable disk-cleanup` activates hooks; migration grandfathers existing user plugins correctly while leaving bundled plugins off.
2026-04-20 04:40:17 -07:00
Plugins occupy one of three states:
feat(plugins): make all plugins opt-in by default Plugins now require explicit consent to load. Discovery still finds every plugin — user-installed, bundled, and pip — so they all show up in `hermes plugins` and `/plugins`, but the loader only instantiates plugins whose name appears in `plugins.enabled` in config.yaml. This removes the previous ambient-execution risk where a newly-installed or bundled plugin could register hooks, tools, and commands on first run without the user opting in. The three-state model is now explicit: enabled — in plugins.enabled, loads on next session disabled — in plugins.disabled, never loads (wins over enabled) not enabled — discovered but never opted in (default for new installs) `hermes plugins install <repo>` prompts "Enable 'name' now? [y/N]" (defaults to no). New `--enable` / `--no-enable` flags skip the prompt for scripted installs. `hermes plugins enable/disable` manage both lists so a disabled plugin stays explicitly off even if something later adds it to enabled. Config migration (schema v20 → v21): existing user plugins already installed under ~/.hermes/plugins/ (minus anything in plugins.disabled) are auto-grandfathered into plugins.enabled so upgrades don't silently break working setups. Bundled plugins are NOT grandfathered — even existing users have to opt in explicitly. Also: HERMES_DISABLE_BUNDLED_PLUGINS env var removed (redundant with opt-in default), cmd_list now shows bundled + user plugins together with their three-state status, interactive UI tags bundled entries [bundled], docs updated across plugins.md and built-in-plugins.md. Validation: 442 plugin/config tests pass. E2E: fresh install discovers disk-cleanup but does not load it; `hermes plugins enable disk-cleanup` activates hooks; migration grandfathers existing user plugins correctly while leaving bundled plugins off.
2026-04-20 04:40:17 -07:00
| State | Meaning | In `plugins.enabled`? | In `plugins.disabled`? |
|---|---|---|---|
| `enabled` | Loaded on next session | Yes | No |
| `disabled` | Explicitly off — won't load even if also in `enabled` | (irrelevant) | Yes |
| `not enabled` | Discovered but never opted in | No | No |
The default for a newly-installed or bundled plugin is `not enabled`. `hermes plugins list` shows all three distinct states so you can tell what's been explicitly turned off vs. what's just waiting to be enabled.
In a running session, `/plugins` shows which plugins are currently loaded.
docs: comprehensive documentation audit — fix 9 HIGH, 20+ MEDIUM gaps (#4087) Reference docs fixes: - cli-commands.md: remove non-existent --provider alibaba, add hermes profile/completion/plugins/mcp to top-level table, add --profile/-p global flag, add --source chat option - slash-commands.md: add /yolo and /commands, fix /q alias conflict (resolves to /queue not /quit), add missing aliases (/bg, /set-home, /reload_mcp, /gateway) - toolsets-reference.md: fix hermes-api-server (not same as hermes-cli, omits clarify/send_message/text_to_speech) - profile-commands.md: fix show name required not optional, --clone-from not --from, add --remove/--name to alias, fix alias path, fix export/ import arg types, remove non-existent fish completion - tools-reference.md: add EXA_API_KEY to web tools requires_env - mcp-config-reference.md: add auth key for OAuth, tool name sanitization - environment-variables.md: add EXA_API_KEY, update provider values - plugins.md: remove non-existent ctx.register_command(), add ctx.inject_message() Feature docs additions: - security.md: add /yolo mode, approval modes (manual/smart/off), configurable timeout, expanded dangerous patterns table - cron.md: add wrap_response config, [SILENT] suppression - mcp.md: add dynamic tool discovery, MCP sampling support - cli.md: add Ctrl+Z suspend, busy_input_mode, tool_preview_length - docker.md: add skills/credential file mounting Messaging platform docs: - telegram.md: add webhook mode, DoH fallback IPs - slack.md: add multi-workspace OAuth support - discord.md: add DISCORD_IGNORE_NO_MENTION - matrix.md: add MSC3245 native voice messages - feishu.md: expand from 129 to 365 lines (encrypt key, verification token, group policy, card actions, media, rate limiting, markdown, troubleshooting) - wecom.md: expand from 86 to 264 lines (per-group allowlists, media, AES decryption, stream replies, reconnection, troubleshooting) Configuration docs: - quickstart.md: add DeepSeek, Copilot, Copilot ACP providers - configuration.md: add DeepSeek provider, Exa web backend, terminal env_passthrough/images, browser.command_timeout, compression params, discord config, security/tirith config, timezone, auxiliary models 21 files changed, ~1000 lines added
2026-03-30 17:15:21 -07:00
## Injecting Messages
Plugins can inject messages into the active conversation using `ctx.inject_message()`:
```python
ctx.inject_message("New data arrived from the webhook", role="user")
```
**Signature:** `ctx.inject_message(content: str, role: str = "user") -> bool`
How it works:
- If the agent is **idle** (waiting for user input), the message is queued as the next input and starts a new turn.
- If the agent is **mid-turn** (actively running), the message interrupts the current operation — the same as a user typing a new message and pressing Enter.
- For non-`"user"` roles, the content is prefixed with `[role]` (e.g. `[system] ...`).
- Returns `True` if the message was queued successfully, `False` if no CLI reference is available (e.g. in gateway mode).
This enables plugins like remote control viewers, messaging bridges, or webhook receivers to feed messages into the conversation from external sources.
:::note
`inject_message` is only available in CLI mode. In gateway mode, there is no CLI reference and the method returns `False`.
:::
See the **[full guide](/docs/guides/build-a-hermes-plugin)** for handler contracts, schema format, hook behavior, error handling, and common mistakes.