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

#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 0a74bfbdea -- confirmed still intact,
no action needed.

Co-authored-by: Markus Hartung <mail@hartmark.se>
This commit is contained in:
Diego Rodrigues de Sa e Souza
2026-08-19 11:08:34 -03:00
committed by GitHub
parent e1c2425ed7
commit 4d92dfe0a2
7 changed files with 367 additions and 9 deletions

View File

@@ -10,6 +10,7 @@ import {
getCurrentHermesAgentRoles,
} from "@/lib/cli-helper/config-generator/hermes-agent";
import { getHermesConfigPath } from "@/lib/cli-helper/config-generator/hermesHome";
import { getApiKeyById } from "@/lib/db/apiKeys";
import { sanitizeErrorMessage } from "@omniroute/open-sse/utils/error.ts";
const hermesAgentSettingsSchema = z.object({
@@ -99,10 +100,29 @@ export async function POST(request: Request) {
await fs.mkdir(configDir, { recursive: true });
// #10711: HermesAgentToolCard's "Apply" flow only ever sends `keyId` (never
// a raw `apiKey`) — the same precedented pattern as claude-settings/route.ts
// and codex-settings/route.ts. Resolve the real key by ID here so
// generateHermesAgentConfig() does not fall through to its
// "YOUR_OMNIROUTE_API_KEY_HERE" placeholder. Never trust a client-supplied
// key string directly: the /api/keys list endpoint returns masked values,
// so the only safe source of a usable key is resolving by ID from the DB.
let resolvedApiKey = apiKey ?? null;
if (keyId) {
try {
const keyRecord = await getApiKeyById(keyId);
if (keyRecord?.key) {
resolvedApiKey = keyRecord.key as string;
}
} catch {
// Non-critical: fall back to whatever apiKey (if any) was already provided.
}
}
const payload = {
baseUrl,
keyId,
apiKey,
apiKey: resolvedApiKey,
selections,
};