Skip to content

feat: pluggable gateway gateway platforms as plugins#17664

Closed
ethernet8023 wants to merge 8 commits into
mainfrom
refactor/gateway-irc
Closed

feat: pluggable gateway gateway platforms as plugins#17664
ethernet8023 wants to merge 8 commits into
mainfrom
refactor/gateway-irc

Conversation

@ethernet8023

Copy link
Copy Markdown
Collaborator

salvage of #7942

Summary

Adds a complete plugin interface for gateway platform adapters. Anyone can now add a new messaging platform (IRC, Viber, Line, custom bridges, etc.) by dropping a plugin directory into ~/.hermes/plugins/ — zero core code changes needed. The plugin uses the same PluginContext as existing plugins, so a single plugin can register a platform adapter AND custom tools, hooks, and CLI commands.

Includes a full IRC adapter as the reference implementation, with an interactive hermes gateway setup wizard , to prove the interface end-to-end.

If all 19 built-in platforms were migrated to plugins, ~48,000 lines would leave the core codebase (~24,500 adapter code + ~21,300 tests + ~2,600 hardcoded glue).

Architecture

Plugin calls ctx.register_platform()
         ↓
PlatformRegistry (gateway/platform_registry.py)
         ↓
Gateway startup checks registry BEFORE the built-in if/elif chain
         ↓
All downstream systems (auth, delivery, toolsets, hints, status, setup)
fall back to the registry for unknown platform names

A plugin platform is just a regular plugin that also calls ctx.register_platform():

def register(ctx):
    # Platform adapter
    ctx.register_platform(name="irc", label="IRC", setup_fn=run_setup_wizard, ...)
    # Optional: platform-specific tools
    ctx.register_tool(name="irc_whois", toolset="irc", ...)
    # Optional: lifecycle hooks
    ctx.register_hook("on_session_start", my_hook)

What Changed (53 files, +4205 / -1038 in PR scope)

Core Infrastructure

File Change
gateway/platform_registry.py NEWPlatformEntry dataclass (now with setup_fn + plugin_name) + PlatformRegistry singleton
gateway/config.py Platform._missing_() restricted to bundled-plugin scan + registry (no arbitrary enum pollution); _PLATFORM_CONNECTED_CHECKERS lambda map + _is_platform_connected(); get_connected_platforms() registry fallback
gateway/run.py _create_adapter() checks registry first; _is_user_authorized() registry fallback; _UPDATE_ALLOWED_PLATFORMS registry check; orphaned config warning; _gateway_runner_ref weakref
hermes_cli/plugins.py PluginContext.register_platform(); auto-stamps plugin_name from manifest; get_bundled_plugins_dir() central helper honoring HERMES_BUNDLED_PLUGINS; PluginManager now scans plugins/platforms/* as its own category root
hermes_cli/gateway.py Unified setup: _unified_platforms() merges built-in _PLATFORMS with registry entries, _configure_platform() shared dispatch, _platform_status() handles plugin platforms via registry check_fn. Plugin platforms surface as "plugin disabled — select to enable" and auto-add to plugins.enabled on selection
hermes_cli/setup.py Dead bespoke wrappers deleted (_setup_whatsapp, _setup_weixin, _setup_email, …) — both hermes setup gateway and hermes gateway setup now read from the same unified list
toolsets.py Auto-generates hermes-<name> toolsets for plugin platforms with _HERMES_CORE_TOOLS + plugin tools
hermes_cli/tools_config.py _get_platform_tools() handles unknown platforms via derived toolset name

Integration Points (18 total — full parity with built-ins)

Integration point How it works
Gateway adapter creation Registry checked before built-in if/elif chain
Config parsing Platform._missing_() accepts registered or bundled plugin names only
Connected platform validation _PLATFORM_CONNECTED_CHECKERS map + registry validate_config()
User authorization PlatformEntry.allowed_users_env / allow_all_env
Cron delivery Platform() resolves any registered name
send_message tool _send_via_adapter() routes through live gateway adapter
Webhook cross-platform delivery Registry checked for known platforms
/update command access PlatformEntry.allow_update_command flag
Channel directory Plugin platforms included in enumeration
System prompt hints PlatformEntry.platform_hint injected into LLM context
Message chunking PlatformEntry.max_message_length for smart splitting
PII redaction PlatformEntry.pii_safe flag
Toolsets Auto-generated hermes-<name> with core tools + plugin tools
hermes status Shows plugin platforms with (plugin) tag
hermes gateway setup / hermes setup gateway Unified flow; plugin platforms appear alongside built-ins, auto-enable on select, dispatch to PlatformEntry.setup_fn
hermes tools / hermes skills Plugin platforms in per-platform config via get_all_platforms()
Token lock (multi-profile) acquire_scoped_lock() pattern documented + implemented in IRC
Orphaned config warning Descriptive log when plugin is enabled in config but missing

IRC Reference Plugin (plugins/platforms/irc/)

  • Full async IRC adapter — stdlib asyncio, zero external dependencies
  • TLS, PING/PONG, nick collision recovery, NickServ auth
  • Channel addressing (nick: msg), DM dispatch, CTCP ACTION
  • Markdown stripping, 512-byte message splitting
  • Scoped token lock, platform hint, auth env vars
  • Config via config.yaml extra dict or IRC_* env vars
  • Interactive setup wizard invoked from hermes gateway setup (server, port, TLS, nick, channels, NickServ)

Documentation

  • website/docs/developer-guide/adding-platform-adapters.md — full "Plugin Path (Recommended)" section with code template, config examples, and 18-row integration table
  • gateway/platforms/ADDING_A_PLATFORM.md — plugin path section with pointer to reference implementation

Tests

  • 101 new tests across platform registry, IRC adapter, IRC setup wizard, plugin platform interface, connectivity checkers, config integration, toolset resolution
  • E2E validation: integration test covering the full plugin lifecycle
  • Updated existing tests for new Platform()-based resolution

How to Use (Plugin Author)

mkdir -p ~/.hermes/plugins/my-platform

Create plugin.yaml + adapter.py (see plugins/platforms/irc/ or the developer guide).

Then either:

hermes gateway setup     # interactive — picks up the plugin automatically

…or configure manually in config.yaml:

plugins:
  enabled: [my-platform]
gateway:
  platforms:
    my_platform:
      enabled: true
      extra:
        token: "..."

That's it. The gateway discovers the plugin, creates the adapter, and routes messages through it.

Design Decisions

  • Registry-first, fallthrough to builtins: Plugin platforms are checked before the if/elif chain. Built-in adapters are completely untouched.
  • Strict dynamic Platform enum: _missing_() only resolves against the bundled plugin scan + registry — arbitrary strings still raise. Cached pseudo-members so Platform("irc") is Platform("irc") holds.
  • Unified setup: One platform list, one dispatch helper. Built-ins and plugin platforms render the same way; plugins without a setup_fn get an env-var display fallback.
  • Auto-enable on select: Plugin platforms surface in setup even when not in plugins.enabled — choosing one adds it to the enabled list before running its setup flow. Removes the chicken-and-egg.
  • Same PluginContext: A platform plugin can also register tools, hooks, and CLI commands — it's a regular plugin that happens to call register_platform().
  • Auto-generated toolsets: resolve_toolset("hermes-irc") returns _HERMES_CORE_TOOLS + any tools registered with toolset="irc". No toolsets.py entry needed.
  • No migration: All 19 built-in adapters stay as-is. This is purely additive.

teknium1 and others added 8 commits April 29, 2026 11:47
Adds a platform adapter plugin interface so anyone can create new gateway
platforms (IRC, Viber, Line, etc.) as drop-in plugins without modifying
core gateway code.

- PlatformEntry dataclass: name, label, adapter_factory, check_fn,
  validate_config, required_env, install_hint, source
- PlatformRegistry singleton with register/unregister/create_adapter
- _create_adapter() in gateway/run.py checks registry first, falls
  through to existing if/elif chain for built-in platforms

- Platform._missing_() accepts unknown string values, creating cached
  pseudo-members so Platform('irc') is Platform('irc') holds true
- GatewayConfig.from_dict() now parses plugin platform names from
  config.yaml without rejecting them
- get_connected_platforms() delegates to registry for unknown platforms

- PluginContext.register_platform() for plugin authors
- Mirrors the existing register_tool() / register_hook() pattern

- Full async IRC adapter using stdlib asyncio (zero external deps)
- Connects via TLS, handles PING/PONG, nick collision, NickServ auth
- Channel messages require addressing (nick: msg), DMs always dispatch
- Markdown stripping for IRC-clean output, message splitting for
  512-byte line limit
- Config via config.yaml extra dict or IRC_* env vars

- Platform enum dynamic members (identity stability, case normalization)
- PlatformRegistry (register, unregister, create, validation, factory)
- GatewayConfig integration (from_dict parsing, get_connected_platforms)
- IRC adapter (init, send, protocol parsing, markdown, requirements)

No existing platform adapters were migrated — the if/elif chain is
untouched. This is Phase 1: prove the interface with a real plugin.
Extends the platform plugin interface from Phase 1 to cover every
touchpoint where built-in platforms have hardcoded behavior.

- allowed_users_env / allow_all_env: per-platform auth env vars
- max_message_length: smart-chunking for send_message tool
- pii_safe: session PII redaction flag
- emoji: CLI/gateway display
- allow_update_command: /update access control

send_message tool (tools/send_message_tool.py):
- Replaced hardcoded platform_map dict with Platform() call
- Added _send_via_adapter() for plugin platforms — routes through
  live gateway adapter when available
- Registry-aware max message length for smart chunking

Cron delivery (cron/scheduler.py):
- Replaced hardcoded 15-entry platform_map with Platform() call
- Plugin platforms now work as cron delivery targets

User authorization (gateway/run.py _is_user_authorized):
- Registry fallback: checks PlatformEntry.allowed_users_env and
  allow_all_env when platform not in hardcoded maps
- Plugin platforms get per-platform auth support

_UPDATE_ALLOWED_PLATFORMS: checks registry allow_update_command flag
Channel directory: includes plugin platforms in session enumeration
Orphaned config warning: descriptive message when plugin platform is
  in config but no plugin registered it
Gateway weakref: _gateway_runner_ref for cross-module adapter access

hermes status: shows plugin platforms with (plugin) tag
hermes gateway setup: plugin platforms appear in menu with setup hints
hermes_cli/platforms.py: get_all_platforms() merges with registry,
  platform_label() falls back to registry for plugin names

- 8 new tests (extended fields, cron resolution, platforms merge)
- Updated 3 tests for new Platform() based resolution
- 2829 passed, 24 pre-existing failures, zero new failures
PII redaction: build_session_context_prompt() now checks the plugin
registry's pii_safe flag in addition to the hardcoded _PII_SAFE_PLATFORMS
frozenset. Plugin platforms that set pii_safe=True (e.g. phone-based
messaging bridges) get their user IDs redacted before LLM context.

Token empty warnings: the empty-token diagnostic at config load now
checks the plugin registry's required_env when a platform isn't in the
hardcoded _token_env_names dict. Catches 'enabled but empty' for
plugin platforms too.
…, docs

Closes remaining functional gaps and adds documentation.

webhook.py: Cross-platform delivery now checks the plugin registry
  for unknown platform names instead of hardcoding 15 names in a tuple.
  Plugin platforms can receive webhook-routed deliveries.

prompt_builder: Platform hints (system prompt LLM guidance) now fall
  back to the plugin registry's platform_hint field. Plugin platforms
  can tell the LLM 'you're on IRC, no markdown.'

PlatformEntry: Added platform_hint field for LLM guidance injection.

IRC adapter: Added acquire_scoped_lock/release_scoped_lock in
  connect/disconnect to prevent two profiles from using the same IRC
  identity. Added platform_hint for IRC-specific LLM guidance.

Removed dead token-empty-warning extension for plugin platforms
  (plugin adapters handle their own env vars via check_fn).

website/docs/developer-guide/adding-platform-adapters.md:
  - Added 'Plugin Path (Recommended)' section with full code examples,
    PLUGIN.yaml template, config.yaml examples, and a table showing all
    18 integration points the plugin system handles automatically
  - Renamed built-in checklist to clarify it's for core contributors

gateway/platforms/ADDING_A_PLATFORM.md:
  - Added Plugin Path section pointing to the reference implementation
    and full docs guide
  - Clarified built-in path is for core contributors only
Plugin platforms now get full toolset support without any entries in
toolsets.py.

tools_config._get_platform_tools(): Falls back to 'hermes-<name>'
  when the platform isn't in the static PLATFORMS dict. No more
  KeyError for plugin platforms.

toolsets.resolve_toolset(): Auto-generates a toolset for plugin
  platforms (hermes-<name>) containing _HERMES_CORE_TOOLS plus any
  tools the plugin registered into a matching toolset name. This means
  a plugin can call ctx.register_tool(toolset='irc', ...) and those
  tools will be included in the hermes-irc toolset automatically.

webhook.py: Registry-aware cross-platform delivery.
run_agent.py: Platform hints from plugin registry.
IRC adapter: Token lock + platform hint.
Removed dead token-empty-warning extension.
Updated docs.
…istry

Merge the two gateway setup paths (hermes setup gateway + hermes gateway
setup) to use a single _unified_platforms() list that merges built-in
_PLATFORMS with dynamically registered plugin entries from
platform_registry.

- Add setup_fn field to PlatformEntry for plugin setup flows
- _unified_platforms() merges built-ins with registry entries by key
- setup_gateway() now uses unified list instead of hardcoded
  _GATEWAY_PLATFORMS tuple list
- gateway_setup() uses same unified list, plugin entries appear
  alongside built-ins with no [plugin] suffix
- _platform_status() handles plugin platforms via registry check_fn
- Plugin platforms with setup_fn get called directly; plugins without
  get a generic env-var display fallback

IRC and other plugin platforms now appear automatically in the setup
menu when registered via platform_registry.register().

feat(gateway): surface disabled platform plugins in setup and auto-enable on select

Platform plugins under plugins/platforms/* (IRC, etc.) were gated behind
plugins.enabled, so `hermes gateway setup` wouldn't list them until the
user ran `hermes plugins enable <name>` first. Now the setup menu always
surfaces them as "plugin disabled — select to enable", and picking one
adds it to plugins.enabled before running its setup flow.

Along the way, unify the two gateway setup flows so `hermes setup gateway`
and `hermes gateway setup` both read from the same platform list (built-in
_PLATFORMS + platform_registry entries), dispatch through a single
_configure_platform() helper, and share _platform_status(). Deletes the
dead bespoke wrappers in setup.py (_setup_whatsapp, _setup_weixin,
_setup_email, etc.) that duplicated logic now covered by the registry
path or _setup_standard_platform.

Also:
- PlatformEntry gains a plugin_name field so the registry knows which
  plugin owns each entry (required for auto-enable).
- PluginContext.register_platform auto-stamps plugin_name from the
  manifest so plugins don't have to pass it explicitly.
- PluginManager now scans plugins/platforms/* as its own category root,
  one level below the bundled plugin scan.
- Fix IRC plugin discovery: rename PLUGIN.yaml → plugin.yaml (the
  scanner is case-sensitive) and add the missing __init__.py that
  _load_directory_module requires.
Nix-built hermes only copied skills/ into the output, so bundled platform
plugins weren't discoverable when running `nix run` (IRC invisible, no
plugin.yaml files present). Mirror the bundled-skills pattern:

- packages.nix: cleanSourceWith plugins/, copy to
  $out/share/hermes-agent/plugins, set HERMES_BUNDLED_PLUGINS on every
  wrapper.
- checks.nix: new bundled-plugins check verifying the directory, a
  sample manifest, and the wrapper env var.
- hermes_cli.plugins.get_bundled_plugins_dir(): central helper that
  honors HERMES_BUNDLED_PLUGINS with a dev-checkout fallback. Used by
  plugins.py, plugins_cmd.py, gateway.py, and web_server.py so every
  call site resolves the same path.
feat(gateway): refine Platform._missing_ and platform-connected dispatch

Restricts plugin-name acceptance to bundled plugin scan + registry
(no arbitrary string -> enum-pollution), pulls per-platform connectivity
checks into a _PLATFORM_CONNECTED_CHECKERS lambda map with a clean
_is_platform_connected method, and adds tests covering the checker map,
plugin platform interface, and IRC setup wizard.
@github-actions

Copy link
Copy Markdown
Contributor

🚨 CRITICAL Supply Chain Risk Detected

This PR contains a pattern that has been used in real supply chain attacks. A maintainer must review the flagged code carefully before merging.

🚨 CRITICAL: Install-hook file added or modified

These files can execute code during package installation or interpreter startup.

Files:

hermes_cli/setup.py

Scanner only fires on high-signal indicators: .pth files, base64+exec/eval combos, subprocess with encoded commands, or install-hook files. Low-signal warnings were removed intentionally — if you're seeing this comment, the finding is worth inspecting.

@github-actions

Copy link
Copy Markdown
Contributor

⚠️ npm lockfile hash out of date

Checked against commit 9cf7c28 (PR head at check time).

The hash = "sha256-..." line in these nix files no longer matches the committed package-lock.json:

Apply the fix

  • Apply lockfile fix — tick to push a commit with the correct hashes to this PR branch
  • Or run the Nix Lockfile Fix workflow manually (pass PR #17664)
  • Or locally: nix run .#fix-lockfiles -- --apply and commit the diff

@alt-glitch alt-glitch added type/feature New feature or request P3 Low — cosmetic, nice to have comp/gateway Gateway runner, session dispatch, delivery comp/plugins Plugin system and bundled plugins comp/cli CLI entry point, hermes_cli/, setup wizard comp/tui Terminal UI (ui-tui/ + tui_gateway/) platform/telegram Telegram bot adapter labels Apr 30, 2026
@alt-glitch

Copy link
Copy Markdown
Collaborator

Related to #12475 (open) and #7942 (closed) — this is a larger salvage/rewrite of the same pluggable platform interface concept.

1 similar comment
@alt-glitch

Copy link
Copy Markdown
Collaborator

Related to #12475 (open) and #7942 (closed) — this is a larger salvage/rewrite of the same pluggable platform interface concept.

teknium1 added a commit that referenced this pull request Apr 30, 2026
Removes drive-by duplication that accumulated during the contributor
branch's multiple rebases. All runtime-benign (dict last-wins,
redefinition last-wins) but left dead source that would confuse
reviewers and maintainers.

Surgical in-place de-duplication (kept PR's intentional additions,
removed only the doubled copy):

* hermes_cli/auth.py: duplicate "gmi" + "azure-foundry" ProviderConfig
* hermes_cli/models.py: duplicate "gmi" entry in _PROVIDER_MODELS
* hermes_cli/config.py: duplicate NOTION/LINEAR/AIRTABLE/TENOR skill env
  block + duplicate get_custom_provider_context_length definition
* hermes_cli/gateway.py: duplicate _setup_yuanbao
* gateway/platforms/base.py: duplicate is_host_excluded_by_no_proxy
* gateway/platforms/telegram.py: duplicate delete_message
* gateway/stream_consumer.py: duplicate _should_send_fresh_final and
  _try_fresh_final
* gateway/run.py: duplicate _parse_reasoning_command_args /
  _resolve_session_reasoning_config / _set_session_reasoning_override,
  duplicate "Drain silently when interrupted" interrupt check
* run_agent.py: duplicate HERMES_AGENT_HELP_GUIDANCE append, duplicate
  codex_message_items capture, duplicate custom_providers resolution
* tools/approval.py: duplicate HARDLINE_PATTERNS section and duplicate
  hardline call in check_dangerous_command
* tools/mcp_tool.py: duplicate _orphan_stdio_pids module-level decl
* cron/scheduler.py: duplicate "not configured/enabled" check — kept
  the new early-rejection, removed the stale late-path copy

Full-file resets to origin/main (all PR additions were duplicates of
content already on main):

* ui-tui/packages/hermes-ink/index.d.ts
* ui-tui/packages/hermes-ink/src/entry-exports.ts
* ui-tui/packages/hermes-ink/src/ink/selection.ts
* ui-tui/src/app/interfaces.ts
* ui-tui/src/app/slash/commands/core.ts
* ui-tui/src/components/thinking.tsx
* ui-tui/src/lib/memoryMonitor.ts
* ui-tui/src/types.ts
* ui-tui/src/types/hermes-ink.d.ts
* tests/hermes_cli/test_doctor.py
* tests/hermes_cli/test_api_key_providers.py
* tests/hermes_cli/test_model_validation.py
* tests/plugins/memory/test_hindsight_provider.py
* tests/run_agent/test_run_agent.py
* tests/gateway/test_email.py
* tests/tools/test_dockerfile_pid1_reaping.py
* hermes_cli/commands.py (slack_native_slashes block — full duplicate)
teknium1 added a commit that referenced this pull request Apr 30, 2026
Removes drive-by duplication that accumulated during the contributor
branch's multiple rebases. All runtime-benign (dict last-wins,
redefinition last-wins) but left dead source that would confuse
reviewers and maintainers.

Surgical in-place de-duplication (kept PR's intentional additions,
removed only the doubled copy):

* hermes_cli/auth.py: duplicate "gmi" + "azure-foundry" ProviderConfig
* hermes_cli/models.py: duplicate "gmi" entry in _PROVIDER_MODELS
* hermes_cli/config.py: duplicate NOTION/LINEAR/AIRTABLE/TENOR skill env
  block + duplicate get_custom_provider_context_length definition
* hermes_cli/gateway.py: duplicate _setup_yuanbao
* gateway/platforms/base.py: duplicate is_host_excluded_by_no_proxy
* gateway/platforms/telegram.py: duplicate delete_message
* gateway/stream_consumer.py: duplicate _should_send_fresh_final and
  _try_fresh_final
* gateway/run.py: duplicate _parse_reasoning_command_args /
  _resolve_session_reasoning_config / _set_session_reasoning_override,
  duplicate "Drain silently when interrupted" interrupt check
* run_agent.py: duplicate HERMES_AGENT_HELP_GUIDANCE append, duplicate
  codex_message_items capture, duplicate custom_providers resolution
* tools/approval.py: duplicate HARDLINE_PATTERNS section and duplicate
  hardline call in check_dangerous_command
* tools/mcp_tool.py: duplicate _orphan_stdio_pids module-level decl
* cron/scheduler.py: duplicate "not configured/enabled" check — kept
  the new early-rejection, removed the stale late-path copy

Full-file resets to origin/main (all PR additions were duplicates of
content already on main):

* ui-tui/packages/hermes-ink/index.d.ts
* ui-tui/packages/hermes-ink/src/entry-exports.ts
* ui-tui/packages/hermes-ink/src/ink/selection.ts
* ui-tui/src/app/interfaces.ts
* ui-tui/src/app/slash/commands/core.ts
* ui-tui/src/components/thinking.tsx
* ui-tui/src/lib/memoryMonitor.ts
* ui-tui/src/types.ts
* ui-tui/src/types/hermes-ink.d.ts
* tests/hermes_cli/test_doctor.py
* tests/hermes_cli/test_api_key_providers.py
* tests/hermes_cli/test_model_validation.py
* tests/plugins/memory/test_hindsight_provider.py
* tests/run_agent/test_run_agent.py
* tests/gateway/test_email.py
* tests/tools/test_dockerfile_pid1_reaping.py
* hermes_cli/commands.py (slack_native_slashes block — full duplicate)
donramon77 added a commit to donramon77/hermes-agent that referenced this pull request May 4, 2026
Upstream v0.12.0 introduced a platform plugin host (28 commits, salvage
of NousResearch#17664) and shipped Microsoft Teams + IRC as the first plugin-based
adapters. The published guidance in
gateway/platforms/ADDING_A_PLATFORM.md and the developer-guide doc is
explicit: plugins are recommended for community/third-party adapters,
built-in is reserved for core contributors. This PR is community.

Migration:

- New layout under plugins/platforms/google_chat/:
  - plugin.yaml (manifest, requires_env)
  - __init__.py (re-exports register)
  - adapter.py (renamed from gateway/platforms/google_chat.py)
  - oauth.py (renamed from gateway/platforms/google_chat_user_oauth.py)
- Internal cross-import google_chat_user_oauth → relative .oauth.
- sys.path.insert hack at the top of the adapter removed; the plugin
  loader handles import paths.
- register(ctx) entry point added: registers the platform with check_fn,
  validate_config, is_connected, required_env, install_hint, allowed-
  users env, max_message_length, emoji, platform_hint.
- check_fn used by the registry pass also requires GOOGLE_CHAT_PROJECT_ID
  and _SUBSCRIPTION_NAME so the platform doesn't appear in cfg.platforms
  when the user hasn't configured Pub/Sub yet (preserves the legacy
  user-facing semantic from the in-tree gate).
- gateway/run.py: removed the Platform.GOOGLE_CHAT arm in
  _create_adapter — the platform_registry lookup at the top of the
  method now drives dispatch.
- tests/gateway/test_google_chat.py: updated imports to point at the
  plugin module paths (gateway.platforms.google_chat ->
  plugins.platforms.google_chat.adapter, _user_oauth -> .oauth).

Backward compatibility:

- Platform.GOOGLE_CHAT enum entry kept in gateway/config.py so existing
  config files and downstream dispatch tables work unchanged.
- Per-user OAuth tokens at ${HERMES_HOME}/google_chat_user_tokens/ and
  the legacy ${HERMES_HOME}/google_chat_user_token.json paths are
  unchanged. No re-authorization required for the production tokens
  already on mac-mini.
- The pip extra [google_chat] is unchanged.

Tests: 130 google_chat tests still pass. Full gateway suite shows no
new regressions (the 11 pre-existing dingtalk/whatsapp failures are
missing-SDK environmental, unrelated).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
nickdlkk pushed a commit to nickdlkk/hermes-agent that referenced this pull request May 11, 2026
…esearch#17664

Removes drive-by duplication that accumulated during the contributor
branch's multiple rebases. All runtime-benign (dict last-wins,
redefinition last-wins) but left dead source that would confuse
reviewers and maintainers.

Surgical in-place de-duplication (kept PR's intentional additions,
removed only the doubled copy):

* hermes_cli/auth.py: duplicate "gmi" + "azure-foundry" ProviderConfig
* hermes_cli/models.py: duplicate "gmi" entry in _PROVIDER_MODELS
* hermes_cli/config.py: duplicate NOTION/LINEAR/AIRTABLE/TENOR skill env
  block + duplicate get_custom_provider_context_length definition
* hermes_cli/gateway.py: duplicate _setup_yuanbao
* gateway/platforms/base.py: duplicate is_host_excluded_by_no_proxy
* gateway/platforms/telegram.py: duplicate delete_message
* gateway/stream_consumer.py: duplicate _should_send_fresh_final and
  _try_fresh_final
* gateway/run.py: duplicate _parse_reasoning_command_args /
  _resolve_session_reasoning_config / _set_session_reasoning_override,
  duplicate "Drain silently when interrupted" interrupt check
* run_agent.py: duplicate HERMES_AGENT_HELP_GUIDANCE append, duplicate
  codex_message_items capture, duplicate custom_providers resolution
* tools/approval.py: duplicate HARDLINE_PATTERNS section and duplicate
  hardline call in check_dangerous_command
* tools/mcp_tool.py: duplicate _orphan_stdio_pids module-level decl
* cron/scheduler.py: duplicate "not configured/enabled" check — kept
  the new early-rejection, removed the stale late-path copy

Full-file resets to origin/main (all PR additions were duplicates of
content already on main):

* ui-tui/packages/hermes-ink/index.d.ts
* ui-tui/packages/hermes-ink/src/entry-exports.ts
* ui-tui/packages/hermes-ink/src/ink/selection.ts
* ui-tui/src/app/interfaces.ts
* ui-tui/src/app/slash/commands/core.ts
* ui-tui/src/components/thinking.tsx
* ui-tui/src/lib/memoryMonitor.ts
* ui-tui/src/types.ts
* ui-tui/src/types/hermes-ink.d.ts
* tests/hermes_cli/test_doctor.py
* tests/hermes_cli/test_api_key_providers.py
* tests/hermes_cli/test_model_validation.py
* tests/plugins/memory/test_hindsight_provider.py
* tests/run_agent/test_run_agent.py
* tests/gateway/test_email.py
* tests/tools/test_dockerfile_pid1_reaping.py
* hermes_cli/commands.py (slack_native_slashes block — full duplicate)
02356abc pushed a commit to 02356abc/hermes-agent that referenced this pull request May 14, 2026
…esearch#17664

Removes drive-by duplication that accumulated during the contributor
branch's multiple rebases. All runtime-benign (dict last-wins,
redefinition last-wins) but left dead source that would confuse
reviewers and maintainers.

Surgical in-place de-duplication (kept PR's intentional additions,
removed only the doubled copy):

* hermes_cli/auth.py: duplicate "gmi" + "azure-foundry" ProviderConfig
* hermes_cli/models.py: duplicate "gmi" entry in _PROVIDER_MODELS
* hermes_cli/config.py: duplicate NOTION/LINEAR/AIRTABLE/TENOR skill env
  block + duplicate get_custom_provider_context_length definition
* hermes_cli/gateway.py: duplicate _setup_yuanbao
* gateway/platforms/base.py: duplicate is_host_excluded_by_no_proxy
* gateway/platforms/telegram.py: duplicate delete_message
* gateway/stream_consumer.py: duplicate _should_send_fresh_final and
  _try_fresh_final
* gateway/run.py: duplicate _parse_reasoning_command_args /
  _resolve_session_reasoning_config / _set_session_reasoning_override,
  duplicate "Drain silently when interrupted" interrupt check
* run_agent.py: duplicate HERMES_AGENT_HELP_GUIDANCE append, duplicate
  codex_message_items capture, duplicate custom_providers resolution
* tools/approval.py: duplicate HARDLINE_PATTERNS section and duplicate
  hardline call in check_dangerous_command
* tools/mcp_tool.py: duplicate _orphan_stdio_pids module-level decl
* cron/scheduler.py: duplicate "not configured/enabled" check — kept
  the new early-rejection, removed the stale late-path copy

Full-file resets to origin/main (all PR additions were duplicates of
content already on main):

* ui-tui/packages/hermes-ink/index.d.ts
* ui-tui/packages/hermes-ink/src/entry-exports.ts
* ui-tui/packages/hermes-ink/src/ink/selection.ts
* ui-tui/src/app/interfaces.ts
* ui-tui/src/app/slash/commands/core.ts
* ui-tui/src/components/thinking.tsx
* ui-tui/src/lib/memoryMonitor.ts
* ui-tui/src/types.ts
* ui-tui/src/types/hermes-ink.d.ts
* tests/hermes_cli/test_doctor.py
* tests/hermes_cli/test_api_key_providers.py
* tests/hermes_cli/test_model_validation.py
* tests/plugins/memory/test_hindsight_provider.py
* tests/run_agent/test_run_agent.py
* tests/gateway/test_email.py
* tests/tools/test_dockerfile_pid1_reaping.py
* hermes_cli/commands.py (slack_native_slashes block — full duplicate)
jsboige pushed a commit to jsboige/hermes-agent that referenced this pull request May 14, 2026
…esearch#17664

Removes drive-by duplication that accumulated during the contributor
branch's multiple rebases. All runtime-benign (dict last-wins,
redefinition last-wins) but left dead source that would confuse
reviewers and maintainers.

Surgical in-place de-duplication (kept PR's intentional additions,
removed only the doubled copy):

* hermes_cli/auth.py: duplicate "gmi" + "azure-foundry" ProviderConfig
* hermes_cli/models.py: duplicate "gmi" entry in _PROVIDER_MODELS
* hermes_cli/config.py: duplicate NOTION/LINEAR/AIRTABLE/TENOR skill env
  block + duplicate get_custom_provider_context_length definition
* hermes_cli/gateway.py: duplicate _setup_yuanbao
* gateway/platforms/base.py: duplicate is_host_excluded_by_no_proxy
* gateway/platforms/telegram.py: duplicate delete_message
* gateway/stream_consumer.py: duplicate _should_send_fresh_final and
  _try_fresh_final
* gateway/run.py: duplicate _parse_reasoning_command_args /
  _resolve_session_reasoning_config / _set_session_reasoning_override,
  duplicate "Drain silently when interrupted" interrupt check
* run_agent.py: duplicate HERMES_AGENT_HELP_GUIDANCE append, duplicate
  codex_message_items capture, duplicate custom_providers resolution
* tools/approval.py: duplicate HARDLINE_PATTERNS section and duplicate
  hardline call in check_dangerous_command
* tools/mcp_tool.py: duplicate _orphan_stdio_pids module-level decl
* cron/scheduler.py: duplicate "not configured/enabled" check — kept
  the new early-rejection, removed the stale late-path copy

Full-file resets to origin/main (all PR additions were duplicates of
content already on main):

* ui-tui/packages/hermes-ink/index.d.ts
* ui-tui/packages/hermes-ink/src/entry-exports.ts
* ui-tui/packages/hermes-ink/src/ink/selection.ts
* ui-tui/src/app/interfaces.ts
* ui-tui/src/app/slash/commands/core.ts
* ui-tui/src/components/thinking.tsx
* ui-tui/src/lib/memoryMonitor.ts
* ui-tui/src/types.ts
* ui-tui/src/types/hermes-ink.d.ts
* tests/hermes_cli/test_doctor.py
* tests/hermes_cli/test_api_key_providers.py
* tests/hermes_cli/test_model_validation.py
* tests/plugins/memory/test_hindsight_provider.py
* tests/run_agent/test_run_agent.py
* tests/gateway/test_email.py
* tests/tools/test_dockerfile_pid1_reaping.py
* hermes_cli/commands.py (slack_native_slashes block — full duplicate)
dannyJ848 pushed a commit to dannyJ848/hermes-agent that referenced this pull request May 17, 2026
…esearch#17664

Removes drive-by duplication that accumulated during the contributor
branch's multiple rebases. All runtime-benign (dict last-wins,
redefinition last-wins) but left dead source that would confuse
reviewers and maintainers.

Surgical in-place de-duplication (kept PR's intentional additions,
removed only the doubled copy):

* hermes_cli/auth.py: duplicate "gmi" + "azure-foundry" ProviderConfig
* hermes_cli/models.py: duplicate "gmi" entry in _PROVIDER_MODELS
* hermes_cli/config.py: duplicate NOTION/LINEAR/AIRTABLE/TENOR skill env
  block + duplicate get_custom_provider_context_length definition
* hermes_cli/gateway.py: duplicate _setup_yuanbao
* gateway/platforms/base.py: duplicate is_host_excluded_by_no_proxy
* gateway/platforms/telegram.py: duplicate delete_message
* gateway/stream_consumer.py: duplicate _should_send_fresh_final and
  _try_fresh_final
* gateway/run.py: duplicate _parse_reasoning_command_args /
  _resolve_session_reasoning_config / _set_session_reasoning_override,
  duplicate "Drain silently when interrupted" interrupt check
* run_agent.py: duplicate HERMES_AGENT_HELP_GUIDANCE append, duplicate
  codex_message_items capture, duplicate custom_providers resolution
* tools/approval.py: duplicate HARDLINE_PATTERNS section and duplicate
  hardline call in check_dangerous_command
* tools/mcp_tool.py: duplicate _orphan_stdio_pids module-level decl
* cron/scheduler.py: duplicate "not configured/enabled" check — kept
  the new early-rejection, removed the stale late-path copy

Full-file resets to origin/main (all PR additions were duplicates of
content already on main):

* ui-tui/packages/hermes-ink/index.d.ts
* ui-tui/packages/hermes-ink/src/entry-exports.ts
* ui-tui/packages/hermes-ink/src/ink/selection.ts
* ui-tui/src/app/interfaces.ts
* ui-tui/src/app/slash/commands/core.ts
* ui-tui/src/components/thinking.tsx
* ui-tui/src/lib/memoryMonitor.ts
* ui-tui/src/types.ts
* ui-tui/src/types/hermes-ink.d.ts
* tests/hermes_cli/test_doctor.py
* tests/hermes_cli/test_api_key_providers.py
* tests/hermes_cli/test_model_validation.py
* tests/plugins/memory/test_hindsight_provider.py
* tests/run_agent/test_run_agent.py
* tests/gateway/test_email.py
* tests/tools/test_dockerfile_pid1_reaping.py
* hermes_cli/commands.py (slack_native_slashes block — full duplicate)
@ethernet8023 ethernet8023 deleted the refactor/gateway-irc branch May 22, 2026 20:36
gweeteve pushed a commit to gweeteve/hermes-agent that referenced this pull request Jun 2, 2026
…esearch#17664

Removes drive-by duplication that accumulated during the contributor
branch's multiple rebases. All runtime-benign (dict last-wins,
redefinition last-wins) but left dead source that would confuse
reviewers and maintainers.

Surgical in-place de-duplication (kept PR's intentional additions,
removed only the doubled copy):

* hermes_cli/auth.py: duplicate "gmi" + "azure-foundry" ProviderConfig
* hermes_cli/models.py: duplicate "gmi" entry in _PROVIDER_MODELS
* hermes_cli/config.py: duplicate NOTION/LINEAR/AIRTABLE/TENOR skill env
  block + duplicate get_custom_provider_context_length definition
* hermes_cli/gateway.py: duplicate _setup_yuanbao
* gateway/platforms/base.py: duplicate is_host_excluded_by_no_proxy
* gateway/platforms/telegram.py: duplicate delete_message
* gateway/stream_consumer.py: duplicate _should_send_fresh_final and
  _try_fresh_final
* gateway/run.py: duplicate _parse_reasoning_command_args /
  _resolve_session_reasoning_config / _set_session_reasoning_override,
  duplicate "Drain silently when interrupted" interrupt check
* run_agent.py: duplicate HERMES_AGENT_HELP_GUIDANCE append, duplicate
  codex_message_items capture, duplicate custom_providers resolution
* tools/approval.py: duplicate HARDLINE_PATTERNS section and duplicate
  hardline call in check_dangerous_command
* tools/mcp_tool.py: duplicate _orphan_stdio_pids module-level decl
* cron/scheduler.py: duplicate "not configured/enabled" check — kept
  the new early-rejection, removed the stale late-path copy

Full-file resets to origin/main (all PR additions were duplicates of
content already on main):

* ui-tui/packages/hermes-ink/index.d.ts
* ui-tui/packages/hermes-ink/src/entry-exports.ts
* ui-tui/packages/hermes-ink/src/ink/selection.ts
* ui-tui/src/app/interfaces.ts
* ui-tui/src/app/slash/commands/core.ts
* ui-tui/src/components/thinking.tsx
* ui-tui/src/lib/memoryMonitor.ts
* ui-tui/src/types.ts
* ui-tui/src/types/hermes-ink.d.ts
* tests/hermes_cli/test_doctor.py
* tests/hermes_cli/test_api_key_providers.py
* tests/hermes_cli/test_model_validation.py
* tests/plugins/memory/test_hindsight_provider.py
* tests/run_agent/test_run_agent.py
* tests/gateway/test_email.py
* tests/tools/test_dockerfile_pid1_reaping.py
* hermes_cli/commands.py (slack_native_slashes block — full duplicate)
Egavasyug pushed a commit to Egavasyug/hermes-agent that referenced this pull request Jun 10, 2026
…esearch#17664

Removes drive-by duplication that accumulated during the contributor
branch's multiple rebases. All runtime-benign (dict last-wins,
redefinition last-wins) but left dead source that would confuse
reviewers and maintainers.

Surgical in-place de-duplication (kept PR's intentional additions,
removed only the doubled copy):

* hermes_cli/auth.py: duplicate "gmi" + "azure-foundry" ProviderConfig
* hermes_cli/models.py: duplicate "gmi" entry in _PROVIDER_MODELS
* hermes_cli/config.py: duplicate NOTION/LINEAR/AIRTABLE/TENOR skill env
  block + duplicate get_custom_provider_context_length definition
* hermes_cli/gateway.py: duplicate _setup_yuanbao
* gateway/platforms/base.py: duplicate is_host_excluded_by_no_proxy
* gateway/platforms/telegram.py: duplicate delete_message
* gateway/stream_consumer.py: duplicate _should_send_fresh_final and
  _try_fresh_final
* gateway/run.py: duplicate _parse_reasoning_command_args /
  _resolve_session_reasoning_config / _set_session_reasoning_override,
  duplicate "Drain silently when interrupted" interrupt check
* run_agent.py: duplicate HERMES_AGENT_HELP_GUIDANCE append, duplicate
  codex_message_items capture, duplicate custom_providers resolution
* tools/approval.py: duplicate HARDLINE_PATTERNS section and duplicate
  hardline call in check_dangerous_command
* tools/mcp_tool.py: duplicate _orphan_stdio_pids module-level decl
* cron/scheduler.py: duplicate "not configured/enabled" check — kept
  the new early-rejection, removed the stale late-path copy

Full-file resets to origin/main (all PR additions were duplicates of
content already on main):

* ui-tui/packages/hermes-ink/index.d.ts
* ui-tui/packages/hermes-ink/src/entry-exports.ts
* ui-tui/packages/hermes-ink/src/ink/selection.ts
* ui-tui/src/app/interfaces.ts
* ui-tui/src/app/slash/commands/core.ts
* ui-tui/src/components/thinking.tsx
* ui-tui/src/lib/memoryMonitor.ts
* ui-tui/src/types.ts
* ui-tui/src/types/hermes-ink.d.ts
* tests/hermes_cli/test_doctor.py
* tests/hermes_cli/test_api_key_providers.py
* tests/hermes_cli/test_model_validation.py
* tests/plugins/memory/test_hindsight_provider.py
* tests/run_agent/test_run_agent.py
* tests/gateway/test_email.py
* tests/tools/test_dockerfile_pid1_reaping.py
* hermes_cli/commands.py (slack_native_slashes block — full duplicate)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

comp/cli CLI entry point, hermes_cli/, setup wizard comp/gateway Gateway runner, session dispatch, delivery comp/plugins Plugin system and bundled plugins comp/tui Terminal UI (ui-tui/ + tui_gateway/) P3 Low — cosmetic, nice to have platform/telegram Telegram bot adapter type/feature New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants