Skip to content

feat(plugins): support config-driven external paths - #38698

Open
sasan1200 wants to merge 1 commit into
NousResearch:mainfrom
sasan1200:codex/upstream-plugin-extra-paths
Open

feat(plugins): support config-driven external paths#38698
sasan1200 wants to merge 1 commit into
NousResearch:mainfrom
sasan1200:codex/upstream-plugin-extra-paths

Conversation

@sasan1200

@sasan1200 sasan1200 commented Jun 4, 2026

Copy link
Copy Markdown

What does this PR do?

Adds config-driven discovery for general plugins stored outside the Hermes source tree and $HERMES_HOME/plugins through plugins.extra_paths in config.yaml.

Configured roots may be collections or direct plugin checkouts. Relative roots resolve from the active Hermes home. External code remains opt-in through plugins.enabled.

Changes

  • Read, canonicalize, and deduplicate plugins.extra_paths.
  • Support collection roots and direct checkouts containing plugin.yaml or plugin.yml.
  • Anchor relative entries to the active Hermes home rather than process CWD.
  • Preserve the existing enable and tool-override consent gates.
  • Prevent unenabled external collisions from hiding active bundled plugins.
  • Warn with both source paths when enabled external roots collide; the later configured root wins deterministically.
  • Align hermes plugins list/enable/disable with runtime discovery.
  • Isolate missing, unreadable, and unresolvable roots.
  • Keep memory, context-engine, model-provider, and dashboard discovery independent.
  • Document activation, precedence, path, and category semantics.

Verification

Rebased directly onto upstream/main at 8defb9fd60bebe2802eaab7c57fa2ee6a4ff6281; current head 9cb676fcfd4495309917aa10cce90d3c901f9c37.

scripts/run_tests.sh \
  tests/hermes_cli/test_plugins.py \
  tests/hermes_cli/test_plugins_cmd.py \
  tests/hermes_cli/test_plugins_cmd_list.py \
  tests/hermes_cli/test_plugins_cmd_enable_disable_nested.py \
  tests/hermes_cli/test_plugins_cmd_category_discovery.py \
  tests/hermes_cli/test_plugins_tts_registration.py \
  tests/hermes_cli/test_plugins_transcription_registration.py

Result: 120 passed across all seven relevant plugin runtime and control-plane files.

.venv/bin/python -m ruff check hermes_cli/plugins.py hermes_cli/plugins_cmd.py \
  tests/hermes_cli/test_plugins.py tests/hermes_cli/test_plugins_cmd_list.py
git diff --check upstream/main...HEAD

Result: passed.

GitHub Actions for this fork head require maintainer approval before jobs can start; no pending check is represented as green.

Risks / mitigations

  • Untrusted code execution: discovery does not execute external plugins until explicitly enabled.
  • Launch-directory drift: relative roots are anchored to the active Hermes home.
  • Bundled suppression: an unenabled later source cannot replace an earlier active manifest.
  • Ambiguous collisions: enabled collisions are deterministic and logged with both sources.
  • Broken mounts: roots are resolved and scanned independently, so one failure cannot hide later valid roots.

Checklist

  • Based on current upstream main
  • Scope limited to general plugin discovery and its control plane
  • Runtime and control-plane suites pass after rebase
  • Documentation and config example updated

@sasan1200
sasan1200 force-pushed the codex/upstream-plugin-extra-paths branch from a463338 to 8148005 Compare June 4, 2026 04:16
@alt-glitch alt-glitch added type/feature New feature or request comp/cli CLI entry point, hermes_cli/, setup wizard comp/plugins Plugin system and bundled plugins P3 Low — cosmetic, nice to have labels Jun 4, 2026
@sasan1200
sasan1200 force-pushed the codex/upstream-plugin-extra-paths branch 7 times, most recently from fe63afe to db27223 Compare June 4, 2026 18:08

@teknium1 teknium1 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for addressing a real plugin-development gap. The premise is confirmed on current main: discovery has no external-root source, and its unconditional winner assignment at hermes_cli/plugins.py:1382 can let an unenabled later manifest suppress an earlier bundled winner before the enable gate at :1452-1466.

Problems

  • HERMES_PLUGIN_PATHS at PR-head hermes_cli/plugins.py:253-255 is a new non-secret, user-facing behavior setting. AGENTS.md:102-107 requires this configuration to live in config.yaml; retain plugins.extra_paths and remove the environment-variable interface.
  • The new scan at PR-head hermes_cli/plugins.py:1154-1163 only reaches PluginManager. Model-provider manifests are deliberately skipped at :1236-1244, but providers/__init__.py:91-171 only scans bundled and $HERMES_HOME provider directories. Memory, context-engine, and dashboard paths likewise have independent discovery, so the advertised external source is incomplete for those plugin categories.

Suggested changes

  • Define whether external roots support all plugin categories. Wire each independent loader to the configured roots with focused tests, or narrow the feature/docs to general PluginManager plugins.
  • Rebase the collision work onto the current loader, which has moved substantially since the PR parent.

Automated hermes-sweeper review.

Comment thread hermes_cli/plugins.py Outdated
Comment thread hermes_cli/plugins.py Outdated
@teknium1 teknium1 added sweeper:risk-security-boundary Sweeper risk: may affect sandboxing, auth, credentials, or sensitive data sweeper:risk-compatibility Sweeper risk: may break existing users, config, migrations, defaults, or upgrades sweeper:blast-moderate Sweeper blast radius: moderate — a subsystem or single platform labels Jul 14, 2026
@sasan1200
sasan1200 force-pushed the codex/upstream-plugin-extra-paths branch 2 times, most recently from f891903 to d87f9af Compare July 14, 2026 17:24
@sasan1200 sasan1200 changed the title feat(plugins): support external plugin paths feat(plugins): support config-driven external paths Jul 14, 2026
@sasan1200
sasan1200 force-pushed the codex/upstream-plugin-extra-paths branch 3 times, most recently from b0396fd to 0085688 Compare July 27, 2026 18:36
@sasan1200

Copy link
Copy Markdown
Author

Rebased onto current upstream main (4b4d2ae4c) and pushed as 00856884f. I also hardened the external-path contract: relative entries now resolve from the active Hermes home, and enabled external key collisions are deterministic and warn with both source paths. The final focused plugin/control-plane suite passes (283 tests), Ruff and diff checks pass, and the full-suite result plus reproduced current-main/macOS failures are documented in the PR body. All existing review threads are resolved.

@sasan1200
sasan1200 force-pushed the codex/upstream-plugin-extra-paths branch 2 times, most recently from 86c4b8b to 0fa4d3e Compare July 27, 2026 19:24
@sasan1200

Copy link
Copy Markdown
Author

Final freshness update: this branch is now rebased directly onto captured upstream/main 5646fed97 and force-with-lease pushed at 0fa4d3ec4.

The review requirements remain fully addressed: there is no HERMES_PLUGIN_PATHS behavioral environment variable, and the supported scope is explicitly limited to general PluginManager plugins rather than implying coverage for the independent memory/context-engine/model-provider/dashboard loaders. Relative paths are anchored to the active Hermes home, activation remains opt-in, and enabled external collisions are deterministic and diagnostic.

Post-rebase verification is clean: 283 passed, Ruff passed, and git diff --check upstream/main...HEAD passed. Both review threads are confirmed resolved through GitHub's review-thread API. The PR body now records the exact final base/head and the earlier full-suite baseline evidence.

@sasan1200
sasan1200 force-pushed the codex/upstream-plugin-extra-paths branch from 0fa4d3e to 596af0e Compare July 27, 2026 22:36

@GottZ GottZ left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This was generated by AI during triage.

Summary

Three PRs cover two distinct plugin improvements: #33363 and #34471 contain the same fix for a curses renderer that ignored its computed scroll offset, while #38698 adds config-driven external general-plugin discovery and prevents unenabled later manifests from shadowing active bundled winners.

Related pull requests

  • #33363 [closed] related — (+184/-7) — superseded by #34471: fixes long-list scrolling by rendering from scroll_offset and adds plugin-list filters and plain/JSON output. It remains relevant because its commit was cherry-picked into the merged salvage PR with authorship preserved.
  • #34471 [merged] related — (+184/-7) — merged reference implementation: carries the #33363 scroll-offset fix, navigation controls, list filters, compact outputs, and focused tests onto current main.
  • #38698 related — (+670/-17) — merge candidate: adds plugins.extra_paths for external general-plugin collections or direct checkouts, aligns runtime and CLI discovery, preserves opt-in activation, and handles collisions and invalid roots deterministically. The earlier keep_open review on #38698 identified an environment-variable interface and overstated category coverage; the current diff removes that interface and explicitly limits and documents support to the general PluginManager, directly addressing both objections.

Duplicates

#33363 and #34471 are substantively identical; #34471 is the merged salvage of #33363.

Suggested consolidation

Merge #38698 after normal final CI/head verification: it addresses the separate external-path discovery gap, and its current diff resolves the contributor's keep_open review concerns without crossing the independent specialized loaders. No additional duplicate needs closing: #33363 is already closed as superseded by merged #34471, while #34471 should remain as the landed reference implementation.

Cross-PR triage: Reviewed 3 pull requests and 0 issues in this complex. Each diff was read against this issue; Assessment working set: 56 kB of PR diffs, 6 kB of issue/PR text, 3 kB of discussion (4 comments), 1 verify verdict. verdicts reflect diff content, not PR titles. Part of an automated triage batch.

@sasan1200
sasan1200 force-pushed the codex/upstream-plugin-extra-paths branch from 596af0e to 9cb676f Compare July 30, 2026 17:29
@sasan1200

Copy link
Copy Markdown
Author

Thanks @GottZ — I completed the requested final current-main verification. This branch is now rebased onto upstream main 8defb9f at head 9cb676fcfd4495309917aa10cce90d3c901f9c37. The seven focused plugin runtime/control-plane files pass 120/120, Ruff and diff checks pass, and a fresh thread audit shows no unresolved review threads. The PR body now contains the exact current evidence. GitHub Actions for this fork head still require maintainer approval before jobs can start.

@sasan1200
sasan1200 force-pushed the codex/upstream-plugin-extra-paths branch from 9cb676f to 992ea0f Compare September 1, 2026 23:28
@sasan1200

Copy link
Copy Markdown
Author

Refreshed #38698 onto current NousResearch/hermes-agent main 180291162ff4df0d42b5dc4fecd08005cf7cebf9 and force-with-lease updated the fork head from 9cb676fcfd4495309917aa10cce90d3c901f9c37 to 992ea0f9d1ecb48dfa4ac050eafc05655b70dfc5.

Scope remains the intended 6 files:

  • cli-config.yaml.example
  • hermes_cli/plugins.py
  • hermes_cli/plugins_cmd.py
  • tests/hermes_cli/test_plugins.py
  • tests/hermes_cli/test_plugins_cmd_list.py
  • website/docs/user-guide/features/plugins.md

Local gates:

  • git diff --check 180291162ff4df0d42b5dc4fecd08005cf7cebf9...HEAD: PASS
  • uv run ruff check hermes_cli/plugins.py hermes_cli/plugins_cmd.py tests/hermes_cli/test_plugins.py tests/hermes_cli/test_plugins_cmd_list.py: PASS
  • uv run pytest tests/hermes_cli/test_plugins.py tests/hermes_cli/test_plugins_cmd_list.py: PASS (95 passed)
  • Full relevant plugin/control-plane files: uv run pytest tests/hermes_cli/test_plugins.py tests/hermes_cli/test_plugins_cmd_list.py tests/hermes_cli/test_plugins_cmd_enable_disable_nested.py tests/hermes_cli/test_plugins_cmd_category_discovery.py tests/hermes_cli/test_plugins_cmd.py tests/hermes_cli/test_plugins_tts_registration.py tests/hermes_cli/test_plugins_transcription_registration.py tests/hermes_cli/test_plugins_hub_perf_guard.py: PASS (174 passed)

Review audit: both existing review threads are resolved; no unresolved review threads remain. The refreshed diff still has no HERMES_PLUGIN_PATHS behavioral env var, keeps support scoped to general PluginManager plugins (not independent memory/context-engine/model-provider/dashboard loaders), resolves relative plugins.extra_paths from the active Hermes home, keeps activation opt-in, and warns with both source paths for enabled external key collisions.

Checks snapshot: GitHub status check rollup is empty/no CI result yet, so this comment does not claim CI green. Post-push readback shows PR open/non-draft, mergeable=MERGEABLE, mergeStateStatus=BLOCKED, base/head as above.

Add plugins.extra_paths discovery for general PluginManager plugins, aligned across runtime and plugin CLI listing while keeping activation opt-in. Relative entries resolve from the active Hermes home, external direct checkouts are supported, independent loader categories stay out of scope, and enabled external key collisions warn with source paths.
@sasan1200
sasan1200 force-pushed the codex/upstream-plugin-extra-paths branch from 992ea0f to 27e2958 Compare September 2, 2026 22:50
@sasan1200

Copy link
Copy Markdown
Author

Refreshed #38698 onto current NousResearch/hermes-agent main 6064668c8fd2dbbb232ea073b32c9d06d932fa56 and force-with-lease updated the fork head from 992ea0f9d1ecb48dfa4ac050eafc05655b70dfc5 to 27e295829f9cd111ebbd7d17531085c1b3cbef6e.

Scope remains the intended 6 files:

  • cli-config.yaml.example
  • hermes_cli/plugins.py
  • hermes_cli/plugins_cmd.py
  • tests/hermes_cli/test_plugins.py
  • tests/hermes_cli/test_plugins_cmd_list.py
  • website/docs/user-guide/features/plugins.md

Local gates against the validated base:

  • git diff --check 6064668c8fd2dbbb232ea073b32c9d06d932fa56...HEAD: PASS
  • uv run ruff check hermes_cli/plugins.py hermes_cli/plugins_cmd.py tests/hermes_cli/test_plugins.py tests/hermes_cli/test_plugins_cmd_list.py: PASS
  • uv run pytest tests/hermes_cli/test_plugins.py tests/hermes_cli/test_plugins_cmd_list.py: PASS (96 passed)
  • uv run pytest tests/hermes_cli/test_plugins.py tests/hermes_cli/test_plugins_cmd_list.py tests/hermes_cli/test_plugins_cmd_enable_disable_nested.py tests/hermes_cli/test_plugins_cmd_category_discovery.py tests/hermes_cli/test_plugins_cmd.py tests/hermes_cli/test_plugins_tts_registration.py tests/hermes_cli/test_plugins_transcription_registration.py tests/hermes_cli/test_plugins_hub_perf_guard.py: PASS (175 passed)

Review audit: GitHub review-thread API reports 2 total review threads and 0 unresolved. The two pending empty reviews are authored by sasan1200 from old state and are not maintainer blockers. The refreshed diff still has no HERMES_PLUGIN_PATHS behavioral env var, keeps support scoped to general PluginManager plugins (not independent memory/context-engine/model-provider/dashboard loaders), resolves relative plugins.extra_paths from the active Hermes home, keeps activation opt-in, and warns with both source paths for enabled external key collisions.

Checks snapshot: GitHub status check rollup is empty/no CI result yet, so this comment does not claim CI green. Post-push readback shows PR open/non-draft, mergeable=MERGEABLE, mergeStateStatus=BLOCKED, base/head as above.

Non-goals: no #38645/#40839 stack changes, no specialized loader path wiring, no generated/dependency/identity-file changes.

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/plugins Plugin system and bundled plugins P3 Low — cosmetic, nice to have sweeper:blast-moderate Sweeper blast radius: moderate — a subsystem or single platform sweeper:risk-compatibility Sweeper risk: may break existing users, config, migrations, defaults, or upgrades sweeper:risk-security-boundary Sweeper risk: may affect sandboxing, auth, credentials, or sensitive data type/feature New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants