Skip to content

fix(honcho): timeout staleness check must resolve from the same sources as the build path - #66428

Merged
teknium1 merged 2 commits into
NousResearch:mainfrom
erosika:fix/honcho-timeout-staleness
Jul 17, 2026
Merged

teknium1 merged 2 commits into
NousResearch:mainfrom
erosika:fix/honcho-timeout-staleness

Conversation

@erosika

@erosika erosika commented Jul 17, 2026

Copy link
Copy Markdown
Contributor

Summary

Follow-up to #66052. The timeout staleness check added there resolves the timeout from different config sources than the build path does, and when the timeout is configured in honcho.json the two permanently disagree — which the check interprets as a config change on every call, tearing down and rebuilding the client singleton each time.

The bug

  • _resolve_timeout_from_sources checked: explicit config → HONCHO_TIMEOUT env → config.yaml → 30.0 default. It never read honcho.json.
  • The build path (from_global_config) resolves the timeout from the honcho.json host block (timeout/requestTimeout) and root-level keys — a supported surface (test_hermes_request_timeout_alias_used asserts it).
  • HonchoSessionManager.honcho calls get_honcho_client() with no config on every property access.

So with e.g. "hosts": {"hermes": {"requestTimeout": 120}}: the client is built with _cached_timeout = 120, the checker resolves 30.0, sees a "change", resets the singleton, and the rebuild lands back on 120 — forever. Every Honcho operation in a long-lived gateway pays a full client rebuild (config re-read, OAuth pre-refresh, new connection pool), which is exactly the hot path the mtime memo in #66052 was added to keep cheap.

The fix

  • _resolve_timeout_from_sources now mirrors the build path exactly: with an explicit config it matches _build (config → config.yaml → default); with no config it matches from_global_config + _build (honcho.json host block/root → env → config.yaml → default). The honcho.json read is memoized on the file's st_mtime_ns so the per-call cost stays one stat(). A genuine honcho.json timeout change is now also detected, extending fix(plugins): rebuild Honcho client when timeout config changes #57437 to that config surface.
  • The config.yaml read now goes through load_config_readonly() instead of a bespoke user-file-mtime memo. The bespoke memo keyed only on the user config.yaml, but load_config() merges the managed-scope config (HERMES_MANAGED_DIR/config.yaml, /etc/hermes) whose leaf keys win — so a managed honcho.timeout with no user file produced the same perpetual-rebuild mismatch, and a managed timeout edit was invisible while the user file's mtime stayed put. load_config_readonly() is already cached on both files' signatures plus the env-ref snapshot, and skips the defensive deepcopy the old memo existed to avoid — no invalidation logic to duplicate.

Validation

Check Result
New regression test (test_honcho_json_timeout_does_not_thrash_singleton) fails on pre-fix client.py, passes with fix verified both directions
New regression test (test_managed_config_timeout_does_not_thrash_singleton, real HERMES_MANAGED_DIR): stable reuse with managed-only timeout + rebuild on managed edit verified both directions
test_timeout_change_triggers_client_rebuild rewritten to drive a real config.yaml (write → rewrite + mtime bump) instead of patching load_config pass
tests/honcho_plugin/ + tests/test_honcho_client_config.py + memory provider suites 550/550

…e the build path

The staleness check added in NousResearch#66052 resolved the timeout from env,
config.yaml, and the default only, while the build path also reads the
honcho.json host block (timeout/requestTimeout). With a timeout
configured in honcho.json, the two permanently disagreed: every
no-config get_honcho_client() call — i.e. every HonchoSessionManager
.honcho property access — interpreted the mismatch as a config change
and tore down and rebuilt the client, defeating the singleton on the
hot path it was meant to protect.

Teach the check to read honcho.json through the same host-aware chain
as from_global_config, memoized on the file's mtime_ns so the per-call
cost stays one stat(). A genuine honcho.json timeout change is now also
detected, extending NousResearch#57437 to that config surface.
@alt-glitch alt-glitch added type/bug Something isn't working comp/plugins Plugin system and bundled plugins tool/memory Memory tool and memory providers area/config Config system, migrations, profiles P3 Low — cosmetic, nice to have sweeper:risk-session-state Sweeper risk: may lose/corrupt/mis-associate session or context state sweeper:risk-compatibility Sweeper risk: may break existing users, config, migrations, defaults, or upgrades labels Jul 17, 2026
…donly

The staleness check's bespoke mtime memo keyed only on the user
config.yaml, but load_config() merges the managed-scope config
(HERMES_MANAGED_DIR/config.yaml, /etc/hermes) whose leaf keys win. A
managed honcho.timeout with no user config.yaml made the memo cache
'no timeout' while _build resolved the managed value — the same
perpetual-rebuild mismatch this PR fixes for honcho.json. A managed
timeout edit was likewise invisible while the user file's mtime stayed
put.

load_config_readonly() is already cached on both files' signatures plus
the env-ref snapshot, so use it instead of duplicating that
invalidation logic; the defensive deepcopy the old memo existed to
avoid is skipped by the readonly variant. Drive the rebuild test
through a real config.yaml and add a HERMES_MANAGED_DIR regression
test covering stable reuse and managed-timeout edits.
@erosika
erosika force-pushed the fix/honcho-timeout-staleness branch from a12d756 to 137ffb2 Compare July 17, 2026 19:38
@teknium1
teknium1 merged commit e4cdd8d into NousResearch:main Jul 17, 2026
33 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area/config Config system, migrations, profiles comp/plugins Plugin system and bundled plugins P3 Low — cosmetic, nice to have sweeper:risk-compatibility Sweeper risk: may break existing users, config, migrations, defaults, or upgrades sweeper:risk-session-state Sweeper risk: may lose/corrupt/mis-associate session or context state tool/memory Memory tool and memory providers type/bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants