Skip to content

fix: distinguish CLI-probe timeouts from not_found, resolve Hermes Agent keyId server-side (#10710+10711) - #10746

Merged
diegosouzapw merged 1 commit into
release/v3.8.50from
fix/10710-cli-tools-timeout-hermes-keyid
Aug 19, 2026
Merged

diegosouzapw merged 1 commit into
release/v3.8.50from
fix/10710-cli-tools-timeout-hermes-keyid

Conversation

@diegosouzapw

Copy link
Copy Markdown
Owner

Closes #10710
Closes #10711

#10710 — CLI-tools all-statuses false negatives / timeout collapse

locateCommand() in src/shared/services/cliRuntime.ts collapsed a genuine
per-probe timeout (runProcess's internal timedOut flag, set when its own
3s timer SIGKILLs the child) into the exact same reason: "not_found" as a
truly-absent binary, on both the Windows where.exe branch and the POSIX
command -v branch. An installed CLI whose probe was starved (e.g. under
the all-statuses route's wide, undeduped ~30-tool fan-out) was reported
identically to one that was never installed at all.

Fix:

  • locateCommand() now returns a distinct reason: "timeout" instead of
    "not_found" on both platform branches when runProcess reports
    timedOut.
  • locateCommandCandidate()'s PATH-search loop no longer treats a timeout
    as terminal — it keeps trying the remaining command aliases (a timeout is
    ambiguous, not proof of absence) and only falls back to reporting the
    timeout if every candidate exhausts without a genuine hit.
  • withSettingsFallback() (cliInstallFallback.ts) now also applies to the
    "timeout" reason, not just "not_found" — a timeout is one more variant
    of "not currently resolvable", the exact scenario that fallback exists
    for.

Bug 2 bundled in this issue (tool-detector.ts's hardcoded ~/.hermes
configPath) was already fixed on release/v3.8.50 by commit
0a74bfbdeae4cc45b34c83d10348e33b7cdd85e4 (one day before #10710 was
filed) — confirmed still intact via the existing hermes-home-env.test.ts
suite, no action needed.

Regression tests: tests/unit/cli-runtime-locate-command-timeout-10710.test.ts
(3 tests — timeout surfaces a distinct reason, a timed-out first candidate
doesn't hide a resolving second candidate, all-candidates-timeout still
reports "timeout" not "not_found").

#10711 — Hermes Agent dashboard writes placeholder API key

The Hermes Agent dashboard "Apply" flow (HermesAgentToolCard.tsx) only
ever sends { keyId, selections } — never a raw apiKey — because
resolving a real key from a stored keyId is expected to happen
server-side (the same pattern claude-settings/route.ts and
codex-settings/route.ts already use). The hermes-agent-settings POST
handler never resolved keyId at all, so generateHermesAgentConfig()
always fell through to the hardcoded placeholder
"YOUR_OMNIROUTE_API_KEY_HERE" for providers.omniroute.api_key,
delegation.api_key, and every auxiliary.*.api_key — yielding 401s
against OmniRoute for every real user of the Apply flow.

Fix: resolve keyId server-side via getApiKeyById() (@/lib/db/apiKeys)
before calling generateHermesAgentConfig(), non-critical try/catch
fallback (never trust a client-supplied key string directly — the
/api/keys list endpoint returns masked values).

Regression tests:

  • tests/unit/cli-helper/hermes-agent-keyid-placeholder-10711.test.ts (2
    tests — confirms the generator-layer fallthrough and that an explicit
    resolved apiKey is written everywhere).
  • tests/unit/hermes-agent-settings-route-keyid-10711.test.ts (2 tests —
    drives the real POST handler end-to-end with a real DB-backed API key and
    asserts the generated YAML carries the real resolved key, plus a
    graceful-fallback case for an unresolvable keyId).

Gates run (all clean on the touched files)

  • node scripts/check/check-file-size.mjs — OK
  • node scripts/check/check-complexity.mjs — OK (2547 violations vs baseline 2774)
  • node scripts/check/check-cognitive-complexity.mjs — OK (1145 violations vs baseline 1223)
  • node scripts/check/check-changelog-integrity.mjs — OK
  • npm run typecheck:core — clean
  • npx eslint --suppressions-location config/quality/eslint-suppressions.json <changed files> — clean
  • node --import tsx/esm --test on all 3 new test files (7 tests) — all green, all confirmed RED before the fix
  • Existing related suites re-run and unaffected: cli-runtime-known-path-shortcircuit-7774, cliRuntime-codex-shebang-8036, cli-helper/tool-detector-win32-7279, repro-6701-claude-detect-fallback, cli-helper/config-generator, cli-catalog-*, cli-tools-schema, cli-helper/hermes-home-env (83 tests, all pass) and tests/integration/all-statuses-route.test.ts (9 tests, all pass)

…ent keyId server-side (#10710+10711)

#10710: locateCommand() in cliRuntime.ts collapsed a genuine probe timeout
(runProcess's timedOut flag) into the same reason:"not_found" as a truly
absent binary, on both the where.exe and `command -v` branches. Give
timeouts a distinct "timeout" reason, keep trying remaining command
candidates in locateCommandCandidate instead of treating a timeout as
terminal, and extend the settings-file fallback (cliInstallFallback.ts) to
also cover the new "timeout" reason, matching the scenario it already
existed for.

#10711: the Hermes Agent dashboard "Apply" flow only ever sends `keyId`
(never a raw `apiKey`), but the hermes-agent-settings POST handler never
resolved it, so generateHermesAgentConfig() always fell through to the
literal placeholder "YOUR_OMNIROUTE_API_KEY_HERE" for
providers.omniroute.api_key, delegation.api_key, and every
auxiliary.*.api_key. Resolve keyId server-side via getApiKeyById(), the
same precedented pattern already used by claude-settings/route.ts and
codex-settings/route.ts.

Bug 2 from #10710 (hermes tool-detector configPath) was already fixed by
commit 0a74bfb -- confirmed still intact,
no action needed.
@diegosouzapw
diegosouzapw merged commit 4d92dfe into release/v3.8.50 Aug 19, 2026
11 checks passed
@diegosouzapw
diegosouzapw deleted the fix/10710-cli-tools-timeout-hermes-keyid branch August 19, 2026 14:08
HouMinXi pushed a commit to HouMinXi/OmniRoute that referenced this pull request Aug 20, 2026
…ent keyId server-side (diegosouzapw#10710+10711) (diegosouzapw#10746)

diegosouzapw#10710: locateCommand() in cliRuntime.ts collapsed a genuine probe timeout
(runProcess's timedOut flag) into the same reason:"not_found" as a truly
absent binary, on both the where.exe and `command -v` branches. Give
timeouts a distinct "timeout" reason, keep trying remaining command
candidates in locateCommandCandidate instead of treating a timeout as
terminal, and extend the settings-file fallback (cliInstallFallback.ts) to
also cover the new "timeout" reason, matching the scenario it already
existed for.

diegosouzapw#10711: the Hermes Agent dashboard "Apply" flow only ever sends `keyId`
(never a raw `apiKey`), but the hermes-agent-settings POST handler never
resolved it, so generateHermesAgentConfig() always fell through to the
literal placeholder "YOUR_OMNIROUTE_API_KEY_HERE" for
providers.omniroute.api_key, delegation.api_key, and every
auxiliary.*.api_key. Resolve keyId server-side via getApiKeyById(), the
same precedented pattern already used by claude-settings/route.ts and
codex-settings/route.ts.

Bug 2 from diegosouzapw#10710 (hermes tool-detector configPath) was already fixed by
commit 0a74bfb -- confirmed still intact,
no action needed.

Co-authored-by: Markus Hartung <mail@hartmark.se>
giauphan pushed a commit to giauphan/OmniRoute that referenced this pull request Aug 20, 2026
…ent keyId server-side (diegosouzapw#10710+10711) (diegosouzapw#10746)

diegosouzapw#10710: locateCommand() in cliRuntime.ts collapsed a genuine probe timeout
(runProcess's timedOut flag) into the same reason:"not_found" as a truly
absent binary, on both the where.exe and `command -v` branches. Give
timeouts a distinct "timeout" reason, keep trying remaining command
candidates in locateCommandCandidate instead of treating a timeout as
terminal, and extend the settings-file fallback (cliInstallFallback.ts) to
also cover the new "timeout" reason, matching the scenario it already
existed for.

diegosouzapw#10711: the Hermes Agent dashboard "Apply" flow only ever sends `keyId`
(never a raw `apiKey`), but the hermes-agent-settings POST handler never
resolved it, so generateHermesAgentConfig() always fell through to the
literal placeholder "YOUR_OMNIROUTE_API_KEY_HERE" for
providers.omniroute.api_key, delegation.api_key, and every
auxiliary.*.api_key. Resolve keyId server-side via getApiKeyById(), the
same precedented pattern already used by claude-settings/route.ts and
codex-settings/route.ts.

Bug 2 from diegosouzapw#10710 (hermes tool-detector configPath) was already fixed by
commit 0a74bfb -- confirmed still intact,
no action needed.

Co-authored-by: Markus Hartung <mail@hartmark.se>
muhamadgalihsaputra pushed a commit to niyatna/NiyatnaRoute that referenced this pull request Sep 27, 2026
…ent keyId server-side (diegosouzapw#10710+10711) (diegosouzapw#10746)

diegosouzapw#10710: locateCommand() in cliRuntime.ts collapsed a genuine probe timeout
(runProcess's timedOut flag) into the same reason:"not_found" as a truly
absent binary, on both the where.exe and `command -v` branches. Give
timeouts a distinct "timeout" reason, keep trying remaining command
candidates in locateCommandCandidate instead of treating a timeout as
terminal, and extend the settings-file fallback (cliInstallFallback.ts) to
also cover the new "timeout" reason, matching the scenario it already
existed for.

diegosouzapw#10711: the Hermes Agent dashboard "Apply" flow only ever sends `keyId`
(never a raw `apiKey`), but the hermes-agent-settings POST handler never
resolved it, so generateHermesAgentConfig() always fell through to the
literal placeholder "YOUR_OMNIROUTE_API_KEY_HERE" for
providers.omniroute.api_key, delegation.api_key, and every
auxiliary.*.api_key. Resolve keyId server-side via getApiKeyById(), the
same precedented pattern already used by claude-settings/route.ts and
codex-settings/route.ts.

Bug 2 from diegosouzapw#10710 (hermes tool-detector configPath) was already fixed by
commit a8e4698 -- confirmed still intact,
no action needed.

Co-authored-by: Markus Hartung <mail@hartmark.se>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

2 participants