hermes-bsd/hermes_cli
teknium1 9b5dae17a5 feat(context-engine): host contract for external context engines
Condenses the substance of PRs #16453, #17453, #16451, #17600, and #13373
into a minimal generic host contract that external context engine plugins
(e.g. hermes-lcm) need to integrate cleanly. Drops scaffolding that
duplicated existing infrastructure or had marginal value.

Five concrete changes:

1. `_transition_context_engine_session()` on AIAgent — generic lifecycle
   helper that fires on_session_end → on_session_reset → on_session_start
   → optional carry_over_new_session_context. Engines implement only the
   hooks they need; missing hooks are skipped. Built-in compressor keeps
   its existing reset-only behavior because callers default to no
   metadata. `reset_session_state()` now optionally accepts
   previous_messages / old_session_id / carry_over_context and delegates
   to the transition helper when provided. (#16453)

2. `conversation_id` passed to `on_session_start()` — both the
   agent-init call site and the compression-boundary call site now
   forward `self._gateway_session_key` so plugin engines have a stable
   conversation identity that survives session_id rotation (compression
   splits, /new, resume). The key already existed on AIAgent; it just
   wasn't reaching engines. (#16453)

3. Canonical cache buckets forwarded to engines — the usage dict passed
   to `update_from_response()` now includes input_tokens, output_tokens,
   cache_read_tokens, cache_write_tokens, and reasoning_tokens on top of
   the legacy prompt/completion/total keys. Engines can make decisions on
   cache-hit ratios and reasoning costs instead of only aggregates. ABC
   docstring updated. (#17453)

4. Plugin-registered context engines visible in the picker —
   `_discover_context_engines()` in plugins_cmd.py now also includes
   engines registered via `ctx.register_context_engine()` from plugin
   manifests, deduplicating by name so repo-shipped descriptions win on
   collision. (#16451)

5. `_EngineCollector.register_command()` — context engines using the
   standard `register(ctx)` pattern can now expose slash commands (e.g.
   `/lcm`). Routes to the global plugin command registry with the same
   conflict-rejection policy regular plugins use (no shadowing built-ins,
   no clobbering other plugins). Previously these calls hit a no-op and
   the slash commands silently never appeared. (#17600)

Dropped from the original 5 PRs:

- Compression boundary signal (`boundary_reason="compression"`) from
  #16453 — already on main at `agent/conversation_compression.py:412-424`,
  landed via the bg-review extraction.

- `discover_plugins()` before fallback in run_agent.py from #16451 —
  redundant: `get_plugin_context_engine()` already routes through
  `_ensure_plugins_discovered()` which is idempotent.

- Runtime identity diagnostics method + helpers from #13373 (+251 LOC) —
  operators can already read engine state via `engine.get_status()`;
  the diagnostics view added marginal value relative to its surface area.

- The 553-LOC slash-command machinery from #17600 — replaced with a
  20-LOC `register_command` method on the collector that reuses the
  existing plugin command registry instead of building a parallel one.

Net: ~215 LOC of host-contract changes + 282 LOC of focused tests, vs
~1,176 LOC across the original 5 PRs.

Co-authored-by: Tosko4 <1294707+Tosko4@users.noreply.github.com>

Closes #16453.
Closes #17453.
Closes #16451.
Closes #17600.
Closes #13373.
Related: stephenschoettler/hermes-lcm#68.
2026-05-28 01:45:30 -07:00
..
dashboard_auth feat(dashboard-auth): HERMES_DASHBOARD_PUBLIC_URL / dashboard.public_url override 2026-05-27 02:12:27 -07:00
proxy docs(auth): replace stale 'hermes login' references with 'hermes auth add' 2026-05-26 15:41:11 -07:00
__init__.py
_parser.py Fix CLI verbose tool progress config fallback 2026-05-23 21:03:51 -07:00
_subprocess_compat.py
auth.py fix(auth): sync manual:device_code Codex pool entries on re-auth (#33744) 2026-05-28 01:33:10 -07:00
auth_commands.py fix(cli): show masked feedback for secret prompts 2026-05-25 01:20:33 -07:00
azure_detect.py
backup.py fix(backup): skip symlinked files in zip archives (#25289) 2026-05-25 05:07:52 -07:00
banner.py fix(docker): bake build-time git SHA into the image 2026-05-28 15:14:05 +10:00
browser_connect.py
build_info.py fix(docker): bake build-time git SHA into the image 2026-05-28 15:14:05 +10:00
bundles.py
callbacks.py fix(cli): show masked feedback for secret prompts 2026-05-25 01:20:33 -07:00
checkpoints.py
claw.py
cli_output.py fix(cli): show masked feedback for secret prompts 2026-05-25 01:20:33 -07:00
clipboard.py
codex_models.py fix(codex): drop dead model slugs that HTTP 400 on ChatGPT Pro (#33424) 2026-05-27 12:16:15 -07:00
codex_runtime_plugin_migration.py
codex_runtime_switch.py
colors.py
commands.py fix: ignore Telegram start pings 2026-05-27 02:41:24 -07:00
completion.py
config.py fix(security): require API_SERVER_KEY before dispatching API server work 2026-05-28 00:25:08 -07:00
container_boot.py fix(docker): make s6 lifecycle work for the unprivileged hermes user 2026-05-25 12:23:23 +10:00
copilot_auth.py
cron.py
curator.py
curses_ui.py
debug.py fix(debug): redact BlueBubbles webhook secrets 2026-05-24 15:43:48 -07:00
default_soul.py
dep_ensure.py
dingtalk_auth.py
doctor.py remove Vercel AI Gateway and Vercel Sandbox (#33067) 2026-05-27 00:43:32 -07:00
dump.py fix(docker): bake build-time git SHA into the image 2026-05-28 15:14:05 +10:00
env_loader.py fix(secrets): only apply external secrets once per HERMES_HOME per process (#32271) 2026-05-25 15:18:55 -07:00
fallback_cmd.py fix(fallback): merge fallback_providers with legacy fallback_model configurations 2026-05-23 05:24:57 -07:00
fallback_config.py fix(fallback): merge fallback_providers with legacy fallback_model configurations 2026-05-23 05:24:57 -07:00
gateway.py feat(docker): auto-redirect gateway run to supervised mode inside s6 image 2026-05-28 12:42:13 +10:00
gateway_windows.py fix(gateway-windows): atomic write for .cmd and startup launcher scripts 2026-05-23 02:30:41 -07:00
goals.py
hooks.py
inventory.py
kanban.py fix(kanban): close kanban.db FD after every connect() in long-lived processes 2026-05-27 22:07:49 -07:00
kanban_db.py test(auth): update entitlement CI expectations 2026-05-28 00:19:31 -07:00
kanban_decompose.py fix(kanban): close kanban.db FD after every connect() in long-lived processes 2026-05-27 22:07:49 -07:00
kanban_diagnostics.py
kanban_specify.py fix(kanban): close kanban.db FD after every connect() in long-lived processes 2026-05-27 22:07:49 -07:00
kanban_swarm.py
logs.py
main.py feat(auth) normalise the way in which we check whether a user has free/paid access to nous portal so we can expose behaviour and error messages accordingly. 2026-05-28 00:19:31 -07:00
mcp_catalog.py feat(mcp): Nous-approved MCP catalog with interactive picker (#30870) 2026-05-26 12:48:14 -07:00
mcp_config.py feat(mcp): Nous-approved MCP catalog with interactive picker (#30870) 2026-05-26 12:48:14 -07:00
mcp_picker.py feat(mcp): Nous-approved MCP catalog with interactive picker (#30870) 2026-05-26 12:48:14 -07:00
memory_setup.py fix(cli): show masked feedback for secret prompts 2026-05-25 01:20:33 -07:00
migrate.py
model_catalog.py
model_normalize.py remove Vercel AI Gateway and Vercel Sandbox (#33067) 2026-05-27 00:43:32 -07:00
model_switch.py
models.py feat(auth) normalise the way in which we check whether a user has free/paid access to nous portal so we can expose behaviour and error messages accordingly. 2026-05-28 00:19:31 -07:00
nous_account.py feat(auth) normalise the way in which we check whether a user has free/paid access to nous portal so we can expose behaviour and error messages accordingly. 2026-05-28 00:19:31 -07:00
nous_subscription.py fix(auth): refresh Nous entitlement in tool menus 2026-05-28 00:19:31 -07:00
oneshot.py fix(provider): make config.yaml model.provider the single source of truth (#31222) 2026-05-23 18:18:41 -07:00
pairing.py
platforms.py
plugins.py feat(plugins): add register_dashboard_auth_provider hook on PluginContext 2026-05-27 02:12:27 -07:00
plugins_cmd.py feat(context-engine): host contract for external context engines 2026-05-28 01:45:30 -07:00
portal_cli.py feat(portal): one-shot setup, status CLI, and Nous-included markers (#30860) 2026-05-23 02:39:09 -07:00
profile_describer.py
profile_distribution.py fix(profile): reject symlinks in distributions (#25292) 2026-05-25 05:07:58 -07:00
profiles.py fix(security): tighten .env file permissions to 0600 at all creation sites 2026-05-25 03:40:47 -07:00
providers.py remove Vercel AI Gateway and Vercel Sandbox (#33067) 2026-05-27 00:43:32 -07:00
pt_input_extras.py
pty_bridge.py
relaunch.py
runtime_provider.py
secret_prompt.py fix(cli): show masked feedback for secret prompts 2026-05-25 01:20:33 -07:00
secrets_cli.py fix(cli): show masked feedback for secret prompts 2026-05-25 01:20:33 -07:00
security_advisories.py
security_audit.py feat(security): on-demand supply-chain audit via OSV.dev (#31460) 2026-05-24 15:15:16 -07:00
send_cmd.py
service_manager.py fix(docker): align HOME for dashboard and s6 gateway services (#33481) 2026-05-28 13:42:27 +10:00
session_recap.py
setup.py remove Vercel AI Gateway and Vercel Sandbox (#33067) 2026-05-27 00:43:32 -07:00
skills_config.py
skills_hub.py fix: backfill official optional skill provenance 2026-05-27 13:39:58 -07:00
skin_engine.py
slack_cli.py
status.py feat(auth) normalise the way in which we check whether a user has free/paid access to nous portal so we can expose behaviour and error messages accordingly. 2026-05-28 00:19:31 -07:00
stdio.py
timeouts.py
tips.py docs(auth): replace stale 'hermes login' references with 'hermes auth add' 2026-05-26 15:41:11 -07:00
tools_config.py fix: expose context engine tools with saved toolsets 2026-05-28 00:28:42 -07:00
uninstall.py
voice.py
web_server.py feat(dashboard-auth): honour X-Forwarded-Prefix + __Host-/__Secure- cookies 2026-05-27 02:12:27 -07:00
webhook.py fix(state): restrict sensitive store file permissions 2026-05-24 04:55:18 -07:00
xai_retirement.py