fix(plugins): isolate profile runtime state - #75648
Conversation
|
Thanks for addressing the shared-runtime profile boundary. The reported premise is present in the inspected checkout: The PR addresses the required boundary by attaching a profile-specific manager to each TUI session and binding it during construction and turns ( Automated hermes-sweeper review. |
|
Thanks for flagging these. #63702 overlaps the plugin-manager portion of this change; #75648 is intended as the consolidation target because the keyed manager cache alone does not isolate the other process-global publication, registry, schema, handler, and runtime-binding surfaces exercised by the shared TUI/WebUI process. #27336 appears complementary rather than duplicative: it updates agent logging-home selection in |
…-deletion (NousResearch#75403) guess_category() classified any file whose name starts with 'test_' or 'tmp_' as disposable, even when the file lived under user-authored directories like patches/, projects/, skins/, or themes/. Files in these trees were silently deleted on session end. Added the missing user-project directories to the exclusion list so that basename-based classification only applies to files in temporary or scratch locations, not durable project trees.
…s from sweep Address review feedback from teknium1: 1. Re-validate stale 'test' category entries in quick() — existing tracked.json entries under now-protected directories (patches/, projects/, etc.) are re-classified via guess_category() and dropped instead of deleted, mirroring the cron-output pattern. 2. Add patches, projects, skins, themes, contributors to _EMPTY_DIR_PROTECTED_TOP_LEVEL so the empty-directory sweep never traverses into these user-authored project trees.
…meout 524ab53 widened the media read timeout from send_video to "all upload send paths" - send_voice/send_audio/send_photo/send_document/send_media_group/ send_animation. Both send_photo calls inside send_image() were missed, so they still ran on the short timeout the rest of the Bot API is tuned for while the sibling media paths already pass it. The missed pair is the worst one to miss: send_image tries a URL send first, then falls back to downloading the image and uploading the bytes - the path documented as "supports up to 10MB", i.e. the slowest send in the file and the one whose server-side processing wait most often outlasts the short budget. When it times out the handler's last resort posts the bare URL as text, so the picture silently never arrives as a picture. Pass _MEDIA_SEND_READ_TIMEOUT on both, covered by two behavioral tests that drive send_image for real - the URL send and the forced byte-upload fallback - and assert the read_timeout that actually reaches the Bot API.
…lay name 7b5a188 migrated the sibling slug sites to custom_provider_slug, which keeps a keyed providers: entry's config key as its durable identity. It covered find_custom_provider_identity_by_model; canonical_custom_identity's third recovery source - the configured-provider fallback - still built f"custom:{normalized}" out of whatever string the caller happened to hold. _get_named_custom_provider matches on either spelling, so a display name that differs from its config key matches the entry and then heals to custom:<display-name>. That is a second identity for one endpoint: the endpoint- and model-based sources of the same function return custom:<config-key>, and so does everything that persists or restores a session's provider override. canonical_custom_identity exists precisely to make a bare "custom" routable again, and tui_gateway calls it on the session-persist, resume and recovery paths - so the divergence lands in stored session identity. Re-resolve through the endpoint the matched entry owns, reusing the function's own URL-based canonicaliser rather than duplicating the match logic. Legacy unkeyed custom_providers: entries keep their name identity, and an unconfigured candidate still returns None.
…toast Toast when a send fails because the disk is full
_is_env_config_key() already routes _API_KEY and _TOKEN suffixed keys to .env for safe credential storage. Add _SECRET to the suffix list so keys like CLIENT_SECRET and ENCRYPTION_SECRET are stored in .env (excluded from git by default) rather than config.yaml.
…th-cancel-race-ia01 fix(web_server): stop Codex OAuth worker from finishing after cancel
setup_path() only wrote a 'hermes' launcher into the command-link dir,
even though pyproject.toml declares three [project.scripts]:
hermes, hermes-agent (run_agent:main), hermes-acp (acp_adapter.entry:main).
Fresh venv installs (the common case on macOS/Linux) leave
~/.local/bin/{hermes-agent,hermes-acp} empty, so external tools
expecting 'hermes-acp' as a standalone command (documented as
first-tier supported in website/docs/user-guide/features/acp.md)
fail to find it after a fully successful install.
Loop over the three console-script names, writing a shim per entry
that exec's the venv interpreter with the right checked-in
entrypoint. --no-venv keeps the old single-shim behaviour since
it does not manage the venv and only 'hermes' is guaranteed on PATH.
Fixes NousResearch#74819
|
Converged PR #75648 to the independently verified current-upstream carry.
The branch update used GitHub's non-force ref update only. The PR body was refreshed, this explanation was posted, any configured review thread received a direct reply, and the branch, body, comment, and thread reply were read back from GitHub before the verified-head receipt was sealed. |
Summary
Fix profile plugin isolation in long-lived shared Hermes runtimes.
Desktop/WebUI sessions can select different profiles inside one backend process. Previously, plugin discovery and registration mutated process-global managers, provider registries, tool handlers, schemas, and caches. Loading one profile could therefore overwrite or expose another profile's capabilities, while runtime profile/home/secret bindings could outlive the turn that established them.
This change:
PluginContextpublication surfaces when registration fails;HERMES_HOMEcorrectly and restores runtime profile/home/secret state after success or exception;Fixes #73230.
Security properties tested
Test plan
Independent QA reviewed exact commit
9db4314ae0ef1e56c9f746113026c113f5b8a551, one commit over4b60979dc188655eb4fb81abf292890147ec2d4c.py_compilefor all 17 changed Python paths: passedgit diff --check, exact parent/tree/path accounting, and clean-worktree checks: passedOptional dependencies were not installed during QA; lazy installation remained disabled.
Scope
One non-merge commit, 17 files, 3,366 insertions and 359 deletions. No deployment, service/configuration change, or local activation is included.
Current-upstream convergence for PR #75648
3d5acc378d1b8d13f7cfb421f5edb11008c18bfd226e27035b73e940151e757daab26c5c53ea45ad1e45aa00859923434de44114ac20ca41a488f191t_3dc37a0dThe replacement head is a non-force fast-forward successor of the prior fork branch. The adapter verified the exact manifest file scope against GitHub before mutation and read back the fork ref and PR body after mutation. No feature scope outside the approved carry manifest was added.