hermes-agent/hermes_cli
Teknium da3a0a852f fix(update): make orphan-backend reap tree-aware + drain Desktop update trees without pre-signalling
Follow-up to #82179 addressing helix4u's review comment
(#82179 issuecomment-5229441571). Three parts:

1. Desktop teardown (salvaged from #77436, @4adwentures): the update
   hand-off's releaseBackendLock() sent SIGTERM to the primary backend
   BEFORE taskkill /T. If the launcher exits first, Windows can no longer
   enumerate its descendants and they survive holding the venv — the
   Electron path that creates the orphan #82179 then has to repair.
   New stopBackendTreesForUpdate() tree-kills the live root first, with
   the behavioral vitest from #77436. The scanner half of #77436 is
   deliberately NOT taken (superseded by #82158's full-cmdline scan).

2. Tree-aware orphan classification: _orphaned_desktop_backend_pids()
   previously refused the whole holder set when any holder had a live
   parent. But the scanner legitimately returns an orphaned serve root
   AND its descendants (the venv trampoline's uv-managed interpreter
   worker — which carries the same backend argv — plus .hermes-runtime
   children). Those have a live parent: the orphan root itself. Now
   holders inside an accepted orphan root's tree fold into that root
   (only roots are returned; taskkill /T reaps descendants), and
   live-parent backends defer to the ancestry check instead of refusing
   outright. Anything outside an orphan tree still refuses.

3. Tests for the mixed shapes: root+managed-runtime child,
   grandchild depth, non-descendant stray alongside an orphan root
   (still refuses), descendant exited mid-classify.

E2E on a real Windows box: spawned a detached backend-shaped orphan
that itself spawned children (3 python descendants); the scanner-shaped
mixed holder set classified to [root], taskkill /T reaped root and all
descendants. The live Desktop backend on the box still classified None
(refusal preserved). The first E2E attempt caught exactly the
trampoline/worker case the mocks missed — the live worker re-execs with
the same backend argv and a live parent — which is what part 2 fixes.

Co-Authored-By: 4adwentures <296413879+4adwentures@users.noreply.github.com>
2026-08-08 20:01:03 -07:00
..
dashboard_auth
…
observability
…
proxy
…
subcommands Port from superagent-ai/grok-cli: verify subsystem (run-recipe detection + environment manifest + hermes verify smoke runner) 2026-08-07 10:11:05 -07:00
web_routers perf(dashboard): keyset pagination for streaming session export 2026-08-08 13:36:08 +05:30
__init__.py
…
_early_recovery.py
…
_parser.py feat: --resume latest keyword and --in DIR launch flag 2026-08-08 04:03:41 -07:00
_scan_venv_blockers.py fix(update): don't truncate cmdlines in the venv-blocker scan — it broke the gateway exemption 2026-08-08 18:58:06 -07:00
_startup_fast.py
…
_subprocess_compat.py
…
active_sessions.py
…
agent_import.py
…
agent_plugins.py fix(plugins): address portable MCP review feedback 2026-08-07 09:44:21 -07:00
approval_mode.py
…
approvals_suggest.py feat(cli): add `hermes approvals test` — dry-run approval verdict CLI 2026-08-07 08:57:39 -07:00
approvals_test.py feat(cli): add `hermes approvals test` — dry-run approval verdict CLI 2026-08-07 08:57:39 -07:00
auth.py fix(auth): read auth stores as UTF-8 to prevent credential loss on Windows 2026-08-08 12:32:23 -07:00
auth_commands.py
…
azure_detect.py
…
backup.py refactor: consolidate five duplicate byte formatters into hermes_cli.sizefmt 2026-08-08 15:10:35 +05:30
bang_shell.py
…
banner.py
…
blueprint_cmd.py refactor(cron): polish registration partial-failure surfaces 2026-08-07 17:45:06 +05:30
browser_connect.py
…
build_info.py
…
bundles.py
…
callbacks.py
…
checkpoints.py refactor: fold simplify findings — 6th copy in update_cmd, drop dead wrapper + speculative kwarg, behavior-contract tests 2026-08-08 15:10:35 +05:30
claw.py
…
cli_agent_setup_mixin.py refactor(sessions): accurate scope wording for tip-only resume rejections 2026-08-08 13:36:08 +05:30
cli_billing_mixin.py
…
cli_commands_mixin.py fix(state,cli,tui-gateway): keep reasoning fields intact across forks and branches 2026-08-08 17:37:26 -07:00
cli_output.py
…
clipboard.py
…
codex_models.py
…
codex_runtime_plugin_migration.py
…
codex_runtime_switch.py
…
colors.py
…
commands.py fix: post-merge audit follow-ups for #81138/#81139/#81141/#81148 2026-08-08 05:21:09 -07:00
completion.py
…
config.py feat(config): resolve ephemeral prompt from display.personality 2026-08-08 14:01:56 -05:00
config_defaults.py feat(compression): native OpenAI Responses server-side compaction for gpt-5.6 2026-08-08 11:24:45 -07:00
config_migrations.py
…
console_engine.py fix: use is_job_runnable/effective_job_state in remaining pause-check sites 2026-08-08 13:48:00 +05:30
container_boot.py
…
context_switch_guard.py
…
copilot_auth.py
…
credential_lifecycle.py
…
cron.py fix(cron): make pause authoritative against half-paused records 2026-08-08 13:48:00 +05:30
curator.py
…
curses_ui.py
…
dashboard_procs.py
…
dashboard_register.py
…
debug.py
…
default_soul.py
…
dep_ensure.py
…
diagnostics_upload.py
…
dingtalk_auth.py
…
doctor.py refactor: fold simplify findings — 6th copy in update_cmd, drop dead wrapper + speculative kwarg, behavior-contract tests 2026-08-08 15:10:35 +05:30
doctor_live.py feat(doctor): add opt-in `hermes doctor --live` real-call backend probes 2026-08-07 09:07:48 -07:00
dump.py
…
env_loader.py fix(cli): strip UTF-8 BOM on latin-1 .env fallback path 2026-08-08 12:32:23 -07:00
fallback_cmd.py
…
fallback_config.py
…
focus_view.py
…
gateway.py
…
gateway_enroll.py
…
gateway_windows.py
…
goals.py fix(goals): decode quality-gate output as UTF-8 instead of the process codepage 2026-08-08 12:34:46 -07:00
gui_uninstall.py
…
heartbeat.py
…
hooks.py Inspired by Cursor: fail-closed hook semantics + exit-code-2 blocking 2026-08-08 05:02:04 -07:00
init_command.py
…
input_sanitize.py
…
inventory.py
…
journey.py
…
kanban.py
…
kanban_db.py feat(kanban,mcp): orphaned-card reconciliation + per-server MCP identity header 2026-08-07 08:58:20 -07:00
kanban_decompose.py
…
kanban_diagnostics.py
…
kanban_specify.py
…
kanban_swarm.py
…
lifecycle.py
…
linux_desktop_entry.py
…
logs.py
…
main.py fix(update): reap orphaned Desktop backends instead of dead-ending the venv-holder guard 2026-08-08 19:46:35 -07:00
managed_scope.py
…
managed_uv.py
…
mcp_catalog.py
…
mcp_config.py
…
mcp_picker.py
…
mcp_security.py
…
mcp_startup.py fix(review): harden portable plugin boundaries 2026-08-07 09:44:21 -07:00
mem_trim.py
…
memory_oauth.py
…
memory_setup.py
…
middleware.py
…
migrate.py
…
moa_cmd.py
…
moa_config.py
…
model_catalog.py
…
model_cost_guard.py
…
model_normalize.py
…
model_search.py
…
model_setup_flows.py fix(model-picker): serve cached custom-provider catalog on no-probe opens (supersedes #81665, #81556) (#81973) 2026-08-08 16:07:03 -04:00
model_switch.py fix(model-switch): route switch_model user-provider key reads through the secret scope 2026-08-08 19:17:05 -07:00
models.py fix(model-picker): serve cached custom-provider catalog on no-probe opens (supersedes #81665, #81556) (#81973) 2026-08-08 16:07:03 -04:00
nous_account.py fix: show explicit member spend cap message instead of 'no credits' 2026-08-07 19:47:40 +05:30
nous_auth_keepalive.py
…
nous_billing.py
…
nous_subscription.py
…
npm_engine.py
…
onepassword_secrets_cli.py
…
oneshot.py fix(cli): scrub lone surrogates before oneshot stdout write 2026-08-08 12:31:19 -07:00
pairing.py
…
partial_compress.py
…
pets.py
…
platforms.py
…
plugins.py fix(plugins): address portable MCP review feedback 2026-08-07 09:44:21 -07:00
plugins_cmd.py fix(gateway): report bundled auto-loading plugins as enabled 2026-08-08 17:34:20 -05:00
portal_cli.py
…
profile_describer.py
…
profile_distribution.py
…
profiles.py
…
projects_cmd.py
…
projects_db.py
…
prompt_size.py
…
prompt_stash.py
…
provider_catalog.py
…
providers.py
…
proxy_cli.py
…
psutil_android.py
…
pt_input_extras.py
…
pty_bridge.py
…
pty_session.py
…
relaunch.py
…
route_identity.py
…
runtime_provider.py
…
secret_prompt.py
…
secrets_cli.py
…
security_advisories.py
…
security_audit.py
…
security_audit_startup.py
…
send_cmd.py fix(cli): apply BOM-safe .env decoding to hermes send's private loader 2026-08-08 12:32:23 -07:00
service_manager.py
…
session_export.py
…
session_export_html.py
…
session_export_md.py
…
session_filters.py
…
session_listing.py
…
session_recap.py
…
session_recovery.py
…
sessions_cmd.py feat(sessions): name a session the moment it starts 2026-08-08 17:07:21 -05:00
setup.py
…
setup_hidden_env.py
…
setup_whatsapp_cloud.py
…
sizefmt.py refactor: fold simplify findings — 6th copy in update_cmd, drop dead wrapper + speculative kwarg, behavior-contract tests 2026-08-08 15:10:35 +05:30
skills_config.py
…
skills_hub.py
…
skin_cmd.py
…
skin_engine.py
…
slack_cli.py
…
slash_exec.py
…
sqlite_runtime.py
…
sqlite_safe_read.py
…
sqlite_util.py
…
status.py feat(cli): global emergency stop — `hermes pause` / `hermes resume` 2026-08-07 08:58:14 -07:00
stdio.py
…
suggestions_cmd.py refactor(cron): polish registration partial-failure surfaces 2026-08-07 17:45:06 +05:30
telegram_managed_bot.py
…
timefmt.py
…
timeouts.py
…
tips.py
…
tools_config.py feat(gateway): key-addressed plugins.manage rows + portable MCP toolset fold-in 2026-08-08 17:24:59 -05:00
toolset_validation.py
…
uninstall.py
…
update_cmd.py fix(update): make orphan-backend reap tree-aware + drain Desktop update trees without pre-signalling 2026-08-08 20:01:03 -07:00
update_lock.py
…
urllib_security.py
…
vercel_auth.py
…
verify_cmd.py Integrate verify subsystem with the existing verification stack 2026-08-07 10:11:05 -07:00
voice.py fix(tts): split long speech by provider and platform limits 2026-08-08 22:54:20 +05:30
web_deps.py
…
web_git.py
…
web_models.py
…
web_server.py fix(dashboard): close config-RMW gaps left by the off-loop sweep 2026-08-08 13:28:28 +05:30
webhook.py
…
win_pty_bridge.py
…
windows_ssh_runtime.py
…
write_approval_commands.py
…
xai_retirement.py
…