Commit Graph

5 Commits

Author SHA1 Message Date
teknium1
746f7ea21b fix(dashboard): Desktop children publish their own host role; SSH spawn shape is Desktop-owned
Review follow-up on the host-rendezvous isolation:

* Excluding Desktop-owned children from ROLE_SERVE broke #119644's terminal
  path: `hermes plugins install` found "the running Desktop backend" only via
  read_record(ROLE_SERVE), so on a Desktop-only box open chats no longer lit
  up. A Desktop child now publishes ROLE_DESKTOP_SERVE (own lock, record and
  0600 token). The attach/refuse ladder (_host_backend_attachment) still reads
  ROLE_SERVE only, so a supervised public dashboard is never blocked by it;
  notify_serve_backend prefers the host owner and falls back to the Desktop
  record. /api/host/identity reports the role actually published.

* is_desktop_owned_backend() missed Desktop's SSH spawn — `env HERMES_DESKTOP=1
  hermes serve --isolated --ssh-session-token-file F` carries NO token env var
  (remote-lifecycle tests assert the var name never appears) — so the SSH child
  still claimed ROLE_SERVE on the remote host and its MCP discovery flipped to
  deferred. The predicate now accepts the token-FILE argv shape; the argv half
  lives in _startup_fast.is_desktop_ssh_backend_argv (stdlib-only, importable
  before main.py's import wall) and replaces the two duplicate substring checks
  in main.py and dashboard_procs.py.

* web_server.py's remaining three bare HERMES_DESKTOP reads (cron ticker,
  managed-gateway teardown, orphan serve reap) route through the predicate;
  the tests that modelled a Desktop child with the bare flag set the spawn
  token, as a real pool child does.
2026-09-23 07:09:45 -07:00
alt-glitch
c07501ec41 fix: two plugins installed together both go live with their MCP tools
The onboarding install card installs its rows concurrently, and each install
runs load_and_go_live: a forced plugin rediscovery, then a read of the plugin's
MCP server configs and skills, then an MCP connect. A forced rediscovery
unloads every plugin first (PluginManager.unload clears _portable_mcp_servers,
_plugin_skills and each server's liveness declaration) and only then loads them
again. When NVIDIA App and NVIDIA Broadcast finished installing together, the
Broadcast pass unloaded the manager while the NVIDIA App go-live was reading
it: NVIDIA App went live with no MCP servers and no skill (the card row said
"Installed" with no tool count, and the build chat had no NVIDIA App tools
until the app restarted).

load_and_go_live now runs one at a time in the process (_GO_LIVE_LOCK), so a
second install's rediscovery cannot run while the first reads or connects.
The connect is inside that lock because it reads the server's liveness
declaration (tools/mcp_tool_transport.py::_live_endpoint), which a forced pass
also clears. The reads share the manager's discovery lock with the pass that
produced them, for forced passes that do not go through load_and_go_live, and
connect_plugin_mcp takes the server configs read there instead of reading the
manager again. The MCP connect stays outside the discovery lock, so chats that
call discover_plugins() are not held for the length of a connect.
2026-09-23 12:45:32 +05:30
Siddharth Balyan
9039b690fc feat: installed plugins' MCP tools and skills are live in every open chat, no Connect-now (#119644)
Installing a plugin from any surface now makes its MCP servers and skills
usable in every open chat of that profile on the chat's next turn. There is
no Connect-now button, no /reload-mcp, no relaunch.

- hermes_cli/plugins_activation.py::load_and_go_live: after the forced plugin
  rescan, connect the plugin's portable MCP servers one by one
  (register_mcp_servers, the connector flow's call), then refresh that
  profile's open chats and queue a turn note listing the servers, their
  tools and the plugin's skills. activation gains live_now; deferred keeps
  only Python tools and prompt sections.
- MCP tools are deferred behind tool_search / tool_call, so appending them
  (preserve_prefix) leaves the model-facing tool array and the cached prompt
  prefix unchanged; the note rides the existing one-shot turn-note channel,
  so the system prompt stays byte-stable. /reload-mcp keeps its consent gate:
  it is a full rebuild.
- tui_gateway/methods_tools.py: one session walk (_refresh_live_sessions)
  shared by reload.mcp and plugin activation, filtered to the plugin's
  profile home.
- hermes plugins install/enable (another process) asks the running Desktop /
  dashboard backend to do the in-process half through the new
  POST /api/dashboard/agent-plugins/activate, found via the host rendezvous
  record and its session token.
- Desktop: the Connect-now toast and its strings are removed in all six
  locales; the install toast says what went live ("3 tools connected ·
  skill X ready") and warns per server that did not connect.
- Contract: PluginActivation.live_now; regenerated TS/OpenRPC.
- register_skill docstring: plugin skills are listed by skills_list.
2026-09-23 05:11:44 +05:30
teknium1
cc02316660 fix: portable MCP server names in the activation summary are the mcp.json names (post-#119263) 2026-09-22 09:50:22 -07:00
teknium1
21d0b12958 feat(plugins): late-loaded plugins wire their platform handlers live (#87770)
A plugin that finished loading after an adapter connected never got its platform
handlers (slash commands, button callbacks, inbound transforms) registered until a
gateway restart, silently. Three pieces, one seam shared by every surface:

1. Discovery listener: PluginManager.on_plugin_loaded(cb) fires from INSIDE
   discover_and_load for the plugins a sweep newly loaded (diff of the loaded set),
   with a per-plugin activation summary (hermes_cli/plugins_activation.py):
   activated_now {gateway_commands, gateway_transforms, hooks, callbacks} vs
   deferred {tools, prompt, mcp_servers}. Every mid-run load path now performs a real
   discover_plugins(force=True): CLI install/enable (via the gateway), Desktop/TUI
   plugins.manage install/toggle/update, dashboard REST install, tool-triggered
   force re-discovery, the new `reload-plugins` control-socket verb. A non-forced
   discover_plugins() short-circuits on _discovered, which is why reload.mcp after
   a mid-run install used to reload the OLD server set.
2. Idempotent re-wire: BasePlatformAdapter.rewire_plugin_handlers() runs only
   factories not yet wired on the live native client (keyed (plugin, qualname);
   a force reload hands back new function objects). Telegram hoists late handlers
   ahead of core's catch-all filters.COMMAND / CallbackQueryHandler (PTB dispatches
   the first match per group) and re-wires on the transient-init rebuild; Slack
   dedupes register_slack_action_handler per AsyncApp. The gateway runner
   subscribes per served profile and re-wires on the loop.
3. Scope limit + honest messaging: handlers only. Tools/prompt stay deferred to
   the next session (prompt-cache invariant), MCP servers to mcp.reload; the CLI
   hint and plugins.manage results (activation, gateway_reloaded,
   restart_required only when no gateway answered) say exactly that.
2026-09-22 09:50:22 -07:00