mirror of
https://github.com/NousResearch/hermes-agent.git
synced 2026-05-18 04:41:56 +00:00
feat(codex-runtime): skip unavailable plugins during migration (#25437)
Followup to PR #24182 — caught when scanning OpenClaw for recent codex fixes we hadn't considered. OpenClaw learned the hard way (#80815) that migrating plugins which codex itself reports as unavailable produces config that fails at activation time. Our /codex-runtime codex_app_server enable path queries codex's plugin/list and migrates everything where installed=true. We were trusting codex's installation state and ignoring its availability field. So a plugin that's installed=true but availability=UNAVAILABLE (broken local install) or REQUIRES_AUTH (OAuth expired or never completed) would get an [plugins."<n>@openai-curated"] entry in ~/.codex/config.toml — and the user's first codex turn after enabling the runtime would fail because codex refuses to activate it. Fix: filter on availability in _query_codex_plugins(). Only emit plugins where availability is empty (older codex versions without the field — preserve backward compat) or explicitly AVAILABLE. Tests: test_plugin_discovery_skips_unavailable_plugins — verifies 4 cases: - good-plugin (installed=True, availability=AVAILABLE) → migrated - broken-plugin (installed=True, availability=UNAVAILABLE) → skipped - auth-pending (installed=True, availability=REQUIRES_AUTH) → skipped - legacy-plugin (installed=True, no availability field) → migrated (older codex versions; preserve backward compat) Docs: Added bullet to 'What's NOT migrated' list in the docs page calling out the availability filter and why. Other OpenClaw codex PRs I reviewed but did NOT apply (with reasoning): - #81591 (load Codex for selectable models): we resolve runtime per-call already, no startup-time gating to fix - #81510 (cron compatibility): we documented cron as untested; their fix is for OpenClaw-specific cron orchestration shape - #81223 (rotate incompatible context-engine threads): we don't have a Lossless context engine equivalent - #80688 (constrain sandbox): we don't have an outer-sandbox concept - #80616 (release on turn_aborted): we already handle status= interrupted in turn/completed correctly - #80278 (expose activeModel in plugin SDK): not our surface - #80792 (default destructive_actions on): we don't expose that knob 56 codex-runtime migration tests still green (+1 new).
This commit is contained in:
parent
f7ad2f1115
commit
d5775fe988
3 changed files with 67 additions and 2 deletions
|
|
@ -353,7 +353,7 @@ class TestMigrate:
|
|||
], None
|
||||
monkeypatch.setattr(crpm, "_query_codex_plugins", fake_query)
|
||||
|
||||
report = migrate({}, codex_home=tmp_path, discover_plugins=True, expose_hermes_tools=False)
|
||||
report = migrate({}, codex_home=tmp_path, discover_plugins=True)
|
||||
text = (tmp_path / "config.toml").read_text()
|
||||
assert '[plugins."github@openai-curated"]' in text
|
||||
assert '[plugins."google-calendar@openai-curated"]' in text
|
||||
|
|
@ -361,6 +361,54 @@ class TestMigrate:
|
|||
assert "google-calendar@openai-curated" in report.migrated_plugins
|
||||
assert "github@openai-curated" in report.migrated_plugins
|
||||
|
||||
def test_plugin_discovery_skips_unavailable_plugins(self):
|
||||
"""Plugins where codex reports availability != AVAILABLE should
|
||||
be skipped — they're broken/uninstallable on codex's side, so
|
||||
migrating them would write config that fails at activation
|
||||
time. Cf. openclaw#80815."""
|
||||
from hermes_cli.codex_runtime_plugin_migration import _query_codex_plugins
|
||||
from unittest.mock import patch
|
||||
|
||||
# Fake a plugin/list response where one plugin is unavailable
|
||||
fake_response = {
|
||||
"marketplaces": [{
|
||||
"name": "openai-curated",
|
||||
"plugins": [
|
||||
{"name": "good-plugin", "installed": True,
|
||||
"enabled": True, "availability": "AVAILABLE"},
|
||||
{"name": "broken-plugin", "installed": True,
|
||||
"enabled": True, "availability": "UNAVAILABLE"},
|
||||
{"name": "auth-pending", "installed": True,
|
||||
"enabled": True, "availability": "REQUIRES_AUTH"},
|
||||
# Plugin without availability field — pass through
|
||||
# (older codex versions or marketplaces that don't
|
||||
# set it should still work).
|
||||
{"name": "legacy-plugin", "installed": True,
|
||||
"enabled": True},
|
||||
]
|
||||
}]
|
||||
}
|
||||
|
||||
class FakeClient:
|
||||
def __init__(self, **kw): pass
|
||||
def initialize(self, **kw): pass
|
||||
def request(self, method, params, timeout=None):
|
||||
return fake_response
|
||||
def close(self): pass
|
||||
def __enter__(self): return self
|
||||
def __exit__(self, *a): pass
|
||||
|
||||
with patch("agent.transports.codex_app_server.CodexAppServerClient",
|
||||
FakeClient):
|
||||
plugins, err = _query_codex_plugins()
|
||||
|
||||
assert err is None
|
||||
names = [p["name"] for p in plugins]
|
||||
assert "good-plugin" in names
|
||||
assert "legacy-plugin" in names # no field → don't skip
|
||||
assert "broken-plugin" not in names
|
||||
assert "auth-pending" not in names
|
||||
|
||||
def test_plugin_discovery_failure_non_fatal(self, tmp_path, monkeypatch):
|
||||
"""If codex isn't installed or RPC fails, MCP migration still
|
||||
completes. The error surfaces in the report but doesn't abort."""
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue