The /tools path builds tool definitions. Part of that work checks whether browser tooling is available. The relevant call chain was:
/tools
↓
get_tool_definitions()
↓
browser availability checks
↓
_find_agent_browser()
↓
_resolve_npx_bin()
↓
npx --versionThe incident was not evidence that npx itself was broken or hung. The evidence showed repeated slow availability probes while the same browser-related definitions were being evaluated.
Why one probe became many
The registry’s availability checks can be evaluated through different closure objects. That means a registry-level check-function cache keyed by function identity did not necessarily deduplicate all calls: two closures with equivalent behavior are still different function objects.
The original /tools timeout regression test captured the user-visible symptom: building the tool definitions could exceed the expected time when the browser checks repeatedly reached the npx --version probe.
This distinction matters. The supported statement is “the probe was repeated and made /tools slow under the tested conditions.” It is not “npx hung.”
The cache boundary
Commit 6f347b18f8b815c19b4b837e0119367f355ce8cb adds a process-local successful-probe cache in tools/browser_tool_install.py. _resolve_npx_bin() computes a key from the executable path, real path, search path, relevant environment, Hermes home, working directory, and executable metadata. A successful node_tool_runnable(npx) result is reused while those inputs remain unchanged.
The cache is intentionally not a permanent installation decision. If the executable changes, the metadata in the key changes and the probe runs again. The regression test writes a temporary executable, calls _resolve_npx_bin() twice, and verifies one probe. It then changes the executable and verifies that a second probe occurs.
Why failed probes are not cached
A failed probe is not a stable fact. The executable may become available, the environment may change, or a managed Node path may be repaired. Caching failure would turn a transient discovery problem into a process-lifetime false negative.
The implementation therefore caches only successful probes. It also bounds the process-local cache to 32 entries and protects it with a lock. The source comment describes the intended scope precisely: reuse success while PATH, environment, and file metadata remain unchanged; never cache failures.
Why registry memoization was insufficient
The registry can cache availability decisions, but the expensive operation was below that boundary. Each closure could still invoke browser availability code, and that code could resolve npx again. The new cache lives at the expensive operation itself, where different callers converge on _resolve_npx_bin().
The regression suite includes a browser-definition pass that invalidates the registry check-function cache between passes and still expects the underlying npx probe to run once. That is the important proof: the lower-level cache works even when the higher-level closure cache is deliberately cleared.
What was actually fixed
The change reduces repeated successful probes within one process. It does not claim that every browser check is free, that npx is always available, or that any failed environment is repaired. If the probe fails, callers still observe the failure and can retry through a later resolution path or process.
The test evidence supports a local performance regression fix, not a benchmarked global /tools latency result. No production timing, request count, or cost number is available in the commit evidence.
Self-review
- The article avoids calling
npxbuggy or hung. - The distinction between registry closure identity and the lower-level process-local cache is grounded in the commit and tests.
- Failed probes are described as intentionally uncached.
- Commit hash and title were checked against Git history.
- The unresolved boundary is behavior outside the tested resolver inputs and environments.
Sources
Hermes repository: tools/browser_tool_install.pyHermes repository: tools/browser_tool.pyHermes repository: tools/registry.pyHermes repository: tests/tools/test_browser_homebrew_paths.pyHermes repository: tests/tools/test_browser_tool.py- Commit
6f347b18f8b815c19b4b837e0119367f355ce8cb—fix: cache browser npx availability probes