Commit Graph

14 Commits

Author SHA1 Message Date
Maciej Lisiewski
d76cce4f11 ci: apply all automatic linter fixes available 2026-10-08 17:22:54 +02:00
Teknium
3a6d709921 fix(plugins): agent builds wait out a slow provider import; only per-turn callers are bounded
The 10 s concurrent-load bound also hit slow but healthy first imports. A
second agent built while a provider's import took longer than 10 s got no
memory provider for its whole session, and with checkpoint_required every
compaction then failed closed.

The bound now applies only inside plugin_loader.bounded_load_wait(), which
the per-turn gateway memory identity signature uses. Other callers (agent
builds) wait for the in-flight load. Cycle detection still breaks a
cross-thread deadlock for every caller.
2026-10-05 15:55:58 -07:00
Teknium
1f11e7fa23 fix(plugins): bound and cycle-check the concurrent plugin load wait
Review fixes for the per-module plugin load lock:

- A second caller waits at most 10 s for another thread's load of the same
  module, then refuses with a named warning and returns None. While that
  import is still stuck, later callers refuse at once. A hung provider import
  no longer blocks every gateway turn, and there is no inline fallback load.
- The wait polls a wait-for graph spanning the loader locks and importlib's
  own module locks. When thread A holds a helper's import lock and waits on
  the plugin while thread B loads the plugin and imports that helper, the
  cycle is detected and the waiter takes the partial module, the same way
  importlib handles a concurrent circular import. Before, both threads hung.
- The lock is still a same-thread RLock with no worker thread, so recursive
  loads and ContextVars are unchanged.
- conversation_compression.py: compact the fork-checkpoint exemption so the
  file stays under its line cap after the rebase.
2026-10-05 15:55:58 -07:00
Yuan Li
decabd79f1 fix(agent): serialize plugin loads and skip fork checkpoints 2026-10-05 15:55:58 -07:00
teknium1
5fa55d1678 feat(plugins): run third-party plugins in a per-profile plugin host (plugins.isolation: host)
plugins.isolation: host imports every non-bundled plugin in a host process started
on demand per profile with only that profile's environment, and hands plugins a
RemotePluginContext; Hermes registers proxies for tools, hooks, commands, skills and
provider objects (subclasses of the ABC it checks). Re-entrant: handlers may call
ctx back into Hermes while Hermes waits on them. Every user-code import path routes
to the host (general, memory/context-engine/cron, model-provider profiles via a
cached credential-free extraction, dashboard APIs via an ASGI bridge) or refuses.
One table names what cannot cross (live gateway objects); the static audit shown
by `hermes plugins validate/show` reads it. A host that dies is restarted with its
plugins (bounded). Default stays in_process: nothing changes unless opted in.

Why: shared-pool deployments must never run one tenant's plugin Python in an
interpreter holding another tenant's data, without plugins becoming tenant-aware.
2026-10-03 10:15:11 -07:00
John Paul Soliva
16db511ed5 perf(config): route the config and manifest loaders through fast_safe_load
utils.fast_safe_load already exists, is pinned by tests/test_fast_safe_load.py, and
its comment names exactly these payers: 'startup parses config.yaml and every plugin
manifest, so the slow path cost ~0.9 s of cold start'. The migration was started —
hermes_cli/config.py uses it eight times, hermes_cli/main.py and hermes_cli/plugins.py
too — but the file-level loaders it was written for were never converted.

The cost is config SIZE, and the size is the installer's doing: it seeds config.yaml
by copying cli-config.yaml.example, 120,897 bytes of mostly comments. Nothing caches
load_gateway_config() and it has 238 production call sites.

Profiled before assuming a cause — reader.forward 34 ms, scanner.scan_to_next_token
28 ms, reader.peek 13 ms: the pure-Python PyYAML scanner, nothing else.

Measured A/B on one realistic pass (gateway config + every bundled plugin
description), medians of five runs, __pycache__ cleared between arms:
48.8 ms -> 2.46 ms. Per path: load_gateway_config 48.3 -> 1.84 ms, managed
config.yaml 44.4 -> 1.03 ms, 105 plugin.yaml manifests 59.3 -> 5.9 ms.

Same parse, same restricted tag set, same result — only the loader changes. Drops the
three 'import yaml' statements the swap orphaned.

(cherry picked from commit a4efd6a4506043159310eba02c32b673c455b88b)
2026-09-23 21:26:49 +05:30
KoNit-K
de6374194c fix(plugins): clean failed sibling modules 2026-09-21 23:26:19 -07:00
teknium1
e3a90079b4 fix(plugins): one unreadable plugin child no longer aborts iter_plugin_dirs or memory-provider discovery
iter_plugin_dirs stat'd <child>/__init__.py inside every plugin dir, so a
single mode-000 / ACL-denied $HERMES_HOME/plugins/<x> still raised
PermissionError out of the loader, memory-provider discovery (dashboard
memory settings, hermes memory setup, plugins memory picker) and the
user cron-provider scan. Catch OSError per child and log the same
'Skipping unreadable plugin directory' warning the list path already emits.

Part of #111804
2026-09-15 18:48:59 -07:00
Teknium
aec3f4bf81 refactor(plugins): schema builder for meet tools, SimpleNamespace bot config, lru-cached cron guard, dispatch/suppress collapses across loader, chronos, node, realtime 2026-09-03 00:00:18 -07:00
Teknium
2aa5c9eee7 refactor(plugins): contextlib.suppress for try/except-pass, fold None guards 2026-09-02 22:44:48 -07:00
Teknium
a47001c8aa refactor(plugins): squeeze statement-separator blanks inside function bodies (AST-neutral) 2026-09-02 22:09:40 -07:00
Teknium
8c1ba7b160 refactor(plugins): drop dead disk_cleanup.deep + unused constants, compact docstrings/comments, tighten guards in google_meet/chronos/loader 2026-09-02 21:34:56 -07:00
Teknium
fc46eabcf5 refactor(plugins): AST-neutral layout compaction of loader, chronos, disk-cleanup, google_meet 2026-09-02 20:47:48 -07:00
Teknium
b54cc716c7 refactor(plugins): shared path-based plugin loader; dedupe cron_providers/context_engine discovery; compact plugin_utils/plugin_storage 2026-09-02 13:30:09 -07:00