Conversation
…arch#70185) The recursive CTE in set_session_archived silently walks the whole compression lineage, so a single sidebar archive can hide days of work in one round-trip with no warning. This commit makes the blast radius visible: * Preview_session_archive_lineage: read-only CTE walk returning cascade_count, cascade_extra, oldest/newest_started_at, and the full affected_ids list. No rows mutated. * web_server PATCH: archive=True with cascade_extra > 0 returns HTTP 409 with the preview payload unless the caller passes confirm_cascade=True. Single-session archives, unarchives, and line-less updates still pass through unchanged — the gate is archive-direction only. * Set_session_archived appends a JSON line to <hermes_home>/logs/archives.jsonl on every successful call so an unexpected cascade leaves a recoverable trail. Best-effort write, profile-aware path resolution, no log entry for failed calls. * Tests: 10 session-archiving scenarios + 4 endpoint scenarios covering 409, confirm path, single-session passthrough, and unarchive bypassing the gate. Archive-signature backward compatible (still bool); existing tests untouched.
NousResearch#74563) The Electron desktop's backend resolver was racing itself on first launch: a synchronous execFileSync('serve --help') probe blocked the boot path for up to 15s on cold Windows installs (Defender scan + venv activation), and a single failed probe poisoned an in-memory cache for the rest of the process, forcing every subsequent launch into the legacy `dashboard --no-open` form even after the runtime was healthy. That fallback then hit the 90s port-announcement deadline and surfaced as 'Desktop boot failed: Timed out waiting for backend port'. Three concrete changes: 1. backendSupportsServe is now async; the exec probe runs through a new probeServeSupport helper in backend-command.ts with a tight 8s ceiling and a 4-way result (true | false | 'timeout' | 'error'). Boot can keep painting while the probe resolves. 2. The serve-support cache stores { value, reason } pairs and logs the reason on every hit, so users can see whether the decision came from the source-scan fast path, the exec probe, or a prior cached entry — without adding custom logging. 3. A new HERMES_DESKTOP_RESET_SERVE_PROBE=1 env var flushes the poisoned cache entry without requiring a desktop restart, the recovery affordance for the exact failure mode in the report. Both getBackendArgsForRuntime call sites (profile launch + reconnect launch) now await the probe so the spawn never sees a stale `serve` argv that the runtime can't parse.
teknium1
left a comment
There was a problem hiding this comment.
Thanks for pursuing the Windows first-launch failure; current main still has the synchronous capability probe at apps/desktop/electron/main.ts:1901, so the async direction addresses a real path.
Problems
apps/desktop/electron/backend-command.ts:105-113dropsbackend.shell. Current-main commit472658d014addedshell: Boolean(backend.shell)because Windows.cmd/.batshims otherwise fail the probe and get cached as unsupported. The new helper must preserve that descriptor property and test it.- The timeout recovery remains poisoned:
apps/desktop/electron/main.ts:1744-1747cachestimeout/errorasfalse. Also, the 8s cap atbackend-command.ts:99is below the ~10.5s cold-start measurement that led current main (74f8e598) to use its shared 15s timeout plus one timeout-only retry. - The added
HERMES_DESKTOP_RESET_SERVE_PROBEconflicts withAGENTS.md:102-107; avoid a new user-facing non-secret environment setting. Do not cache transient probe outcomes instead. - The unrelated session-archiving commit
e2d73f2ca920should be kept separate from this desktop salvage.
Suggested changes
- Preserve
shelland resolvedHERMES_HOMEin the async probe, cache only definitive capability results, and add Windows.cmdplus timeout-cache regression coverage.
Automated hermes-sweeper review.
| [...prefix, 'serve', '--help'], | ||
| { | ||
| cwd: options.cwd ?? backend.root, | ||
| env: { ...process.env, ...(options.extraEnv ?? {}), ...(backend.env ?? {}) }, |
There was a problem hiding this comment.
This probe must use the same resolved HERMES_HOME as the real backend spawn. Current main.ts:1903 injects it explicitly because the desktop may resolve a home that is not present in process.env; this merge only retains process.env and backend.env.
| cwd: options.cwd ?? backend.root, | ||
| env: { ...process.env, ...(options.extraEnv ?? {}), ...(backend.env ?? {}) }, | ||
| timeout: timeoutMs, | ||
| windowsHide: true, |
There was a problem hiding this comment.
Please preserve shell: Boolean(backend.shell) here and add shell to the descriptor type. Current-main commit 472658d014 fixed this exact .cmd/.bat Windows failure: without it, execFile can reject the shim and falsely classify a modern runtime as lacking serve.
| const unsupportedReason = `exec-probe: ${probeDetail}` | ||
| const entry: _ServeCacheEntry = { value: false, reason: unsupportedReason } | ||
|
|
||
| _serveSupportCache.set(key, entry) |
There was a problem hiding this comment.
This still caches a transient timeout or spawn error as false, recreating the poisoned-cache failure the change describes. Cache only definitive unsupported results; retry transient outcomes on the next resolution instead of requiring a new environment-variable reset.
SummaryOne PR addresses #74563. #74603 makes the serve-capability probe asynchronous and adds decision logging, but its current implementation can still misclassify a working Windows runtime and reproduce the reported dashboard fallback. Related pull requests
Suggested consolidationAuthor action: rebase #74603 onto main, or split out the part that can merge. Preserve the async probe and diagnostic logging, but retain Complex graphflowchart LR
classDef open fill:#dbeafe,stroke:#1d4ed8,color:#1e3a8a
classDef merged fill:#dcfce7,stroke:#15803d,color:#14532d
classDef closed fill:#e5e7eb,stroke:#6b7280,color:#1f2937
classDef unverified fill:#f3f4f6,stroke:#9ca3af,color:#374151
classDef best stroke-width:3px,stroke:#b45309
classDef target stroke-width:3px,stroke:#4338ca
I74563(["issue #74563 (open)"])
P74603["PR #74603 (open)"]
P74603 -->|best fix| I74563
class I74563 open
class P74603 open
class P74603 best
class P74603 target
click I74563 "https://github.com/NousResearch/hermes-agent/issues/74563"
click P74603 "https://github.com/NousResearch/hermes-agent/pull/74603"
Graph: solid arrow = fixes / best fix, dashed arrow = partial or unverified (see edge label); boxed group = PRs duplicating each other; amber border = best fix; indigo border = target; gray node = closed (state tag in the node label). Cross-PR triage: Reviewed 1 pull request and 1 issue in this complex. Each diff was read against this issue; Assessment working set: 43 kB of PR diffs, 6 kB of issue/PR text, 2 kB of discussion (4 comments), 2 verify verdicts. verdicts reflect diff content, not PR titles. Part of an automated triage batch. |
|
Cross-linking the class: #76958 (desktop boot/token class) edits the failure-latch path of main.ts (~8292/8628/8671/9742) plus backend-start-failure.ts; this PR edits the probe path (~1648/7599/7856). Disjoint regions of the same file — they compose; the second to merge needs only a trivial rebase. To clear the sweeper's keep_open points:
Both PRs target the same boot reliability class — landing both closes it. |
Summary
The Electron desktop's backend resolver was racing itself on first launch: a synchronous
execFileSync('serve --help')probe blocked the boot path for up to 15s on cold Windows installs (Defender scan + venv activation), and a single failed probe poisoned an in-memory cache for the rest of the process. That forced every subsequent launch into the legacydashboard --no-openform, which then hit the 90s port-announcement deadline and surfaced as the reported "Desktop boot failed: Timed out waiting for backend port (90000ms)". After that one slow probe, the cache pinned the runtime todashboard --no-openfor the rest of the desktop session — the exact "inconsistent resolution" symptom in the issue.Three concrete changes:
backendSupportsServeis now async; the exec probe runs through a newprobeServeSupporthelper with a tight 8s ceiling and a 4-way result (true | false | 'timeout' | 'error'). Boot can keep painting while the probe resolves.{ value, reason }pairs and logs the reason on every hit (source-scan: add_parser("serve") matched,exec-probe: serve --help exited 0,cache: ..., etc.), so users can diagnose without adding custom logging.HERMES_DESKTOP_RESET_SERVE_PROBE=1env var flushes the poisoned cache entry without requiring a desktop restart — the recovery affordance for the exact failure mode in the report.Both
getBackendArgsForRuntimecall sites (profile launch + reconnect launch) nowawaitthe probe so the spawn never sees a staleserveargv that the runtime can't parse.Changes
apps/desktop/electron/backend-command.ts— newprobeServeSupport(backend, options)async helper +ServeProbeResultdiscriminated union; stdio is[ignore, ignore, ignore]so a probe's--helptext never bleeds into the real backend's log buffer.apps/desktop/electron/main.ts—backendSupportsServerewritten as async, returning{ supported, reason };_serveSupportCachenow stores{ value, reason }entries;HERMES_DESKTOP_RESET_SERVE_PROBE=1escape hatch; bothgetBackendArgsForRuntimecall sites awaited.apps/desktop/electron/backend-command.test.ts— 6 new unit tests forprobeServeSupport(true / false / timeout / ENOENT / empty backend /-mprefix preservation).How to Test
Expected: all existing tests + 6 new
probeServeSupporttests pass.Manual repro of the issue on a Windows box:
.pyc).desktop.logfor[backend] \serve` ... source scan failed for ... falling through to exec probefollowed by[backend] `serve` unsupported for ... — exec-probe: timeout`.HERMES_DESKTOP_RESET_SERVE_PROBE=1and relaunch. The next launch should resolve toserve(per the source scan fast path).Checklist
vitest; reviewer should run on macOS/Linux/Windows)apps/desktop/electron/).envnot used —HERMES_DESKTOP_RESET_SERVE_PROBEis a debug escape hatch documented in code comments; users don't need to set it unless recovering from a poisoned cacheRisk & Impact
Low. The change replaces a synchronous, blocking probe with an async one of equivalent semantics — every code path that returned
true/falsebefore still does, but with a documented reason attached. The only user-visible behavior change is (a) boot no longer blocks on a 15s synchronous probe (it awaits an 8s async probe, with the splash still painting), and (b) the decision is now diagnosable fromdesktop.logalone.Type: Bug fix
Closes #74563