Skip to content

fix(gateway): resolve quota footer conflicts with current upstream main - #2

Closed
sfire123 wants to merge 237 commits into
lancecheney:feat/runtime-footer-quotafrom
sfire123:fix/pr18188-main-conflicts
Closed

sfire123 wants to merge 237 commits into
lancecheney:feat/runtime-footer-quotafrom
sfire123:fix/pr18188-main-conflicts

Conversation

@sfire123

@sfire123 sfire123 commented Sep 9, 2026

Copy link
Copy Markdown

Summary

Resolve the conflict blocking NousResearch#18188 by merging upstream main at facb9eb4f17735a7d70f28cb6a26d95ffdc4c105 into the existing feat/runtime-footer-quota head 3e5500a5694e7b2b0c02b667594dca81fda8437d.

There was one content conflict, in gateway/run_turn_runner.py: both branches added independent declarations after the module logger. This merge preserves both:

  • _resolve_runtime_footer_metadata, including profile-scoped, runtime-credential-aware account/quota lookup and the no-raw-key result contract;
  • upstream _ExecApprovalDeclined and all egress-decline handling, so a refused approval destination does not receive a plain-text fallback or leave approval work pending.

No footer capability or upstream authorization behavior was removed. The quota feature remains opt-in; default footer fields and stale-while-revalidate behavior are unchanged. The diff against upstream main remains the existing 11-file footer feature, not a new implementation.

Integration

This is a merge-based refresh rather than a rebase because the contributor cannot push directly to the author's branch. Original commits and authorship are preserved. Please merge normally (not squash), or fast-forward feat/runtime-footer-quota to this branch, to preserve the upstream-main ancestry and clear the conflict in NousResearch#18188.

Published head: cb38c7662289e5f07e9f0706c20ce82dc23f8d3d.

Verification

Canonical per-file test runner, 8 focused files: 143 passed, 0 failed:

  • tests/gateway/test_runtime_footer.py
  • tests/gateway/test_runtime_footer_usage_cache.py
  • tests/gateway/test_runtime_footer_runner_wiring.py
  • tests/test_account_usage.py
  • tests/agent/test_account_usage.py
  • tests/gateway/test_prompt_decline_no_fallback.py
  • tests/gateway/test_decline_fallback_suppression.py
  • tests/gateway/relay/test_relay_egress_declines.py

Additional checks:

  • Ruff passed on all 10 Python files in the footer diff.
  • Windows-footgun scan passed on those 10 files.
  • Canonical runner byte-compilation passed.
  • git diff --check origin/main...HEAD passed.
  • Both the original PR head and the fetched upstream-main head are ancestors of the new merge commit.

An unrelated existing trailing-space line in upstream tests/install/windows-e2e.ps1 was left unchanged; the footer diff relative to main is whitespace-clean. The full repository suite was not run; these are local results, not GitHub-hosted CI.

ethernet8023 and others added 30 commits August 10, 2026 17:59
… commit via fake git remote

windows sibling of install-e2e-run.yml. no bubblewrap on windows, so the
git proxying is git's own transport rewrite: an isolated GIT_CONFIG_GLOBAL
with multi-valued url.<file://fake.git>.insteadOf for both hardcoded repo
URLs, so the published Hermes-Setup.exe's install.ps1 clone, hermes update's
fetch, and the desktop's ls-remote all land on a local bare repo whose main
the driver controls - installer and updater run verbatim.

one run: seed fake.git from the checkout, force fake main to the newest
release tag, drive the real published bootstrap installer with AutoHotkey
(GUI, no headless mode), promote fake main to HEAD, then apply the desktop
app's builtin update route (scripts/desktop-update.ps1 -NoUi when the
installed base ships it, staged hermes-setup.exe --update otherwise) and
assert HEAD == target with a working hermes.

TODO routes: bare hermes update, and re-running the bootstrap installer
over the existing checkout.
… the checkout trip the driver's dirty-tree guard
…licking Install

AutoHotkey64 is a GUI-subsystem exe: spawned without -NoNewWindow it has
no console, FileAppend('*') throws '(6) The handle is invalid' on the first
Log call, and OnError's own Log rethrows inside the handler - the script
hangs with the error tooltip painted over the installer and Install is
never clicked (confirmed from the run 31443096241 screen recording; ahk.log
was never created because the stdout write preceded the file write).

Wrap the stdout append in try (the log file is the record) and spawn the
helper with -NoNewWindow so its live lines reach the job log.
…d installer

Run 31445244722's recording shows the published Hermes-Setup.exe renders
'[ INSTALL ]' as flat blue text on off-white - nothing like the solid-blue
'Install Hermes ->' reference from the dev-build era, so ImageSearch never
matched. Replace the reference with a crop of the real button taken from
that recording (tolerance *60 to absorb H.264 drift, click retried across
animation frames), and drop launch-button.png entirely: completion now
polls the installer's own bootstrap-complete marker
(.hermes-bootstrap-complete, see paths.rs likely_bootstrap_marker), which
cannot go stale with a UI restyle.
…attempt

Run 31445907233's recording shows the runner session's maximized console
covering the installer for the whole run: WinWait matches by title
regardless of z-order, but ImageSearch reads screen pixels, so the Install
button was never visible to it. WinActivate + WinMoveTop before every
attempt; run 31443096241 already proved the same reference crop renders
match-ably when the window is frontmost.
Run 31446292343 disproved the z-order theory: the recording shows the
installer frontmost, red click-marker dots painting on it, and the button
rendered - yet ImageSearch missed on all 5 attempts. The reference crop is
the problem: it came from an H.264/yuv420p recording whose chroma
subsampling smears glyph edges. Diffing the crop against run 5's OWN
recording of the same screen gives max 8 shades/channel (matches easily),
so the crop is video-faithful but not screen-faithful, and no tolerance
fixes that reliably.

Keep ImageSearch as the first try, but fall back to PixelSearch for the
button text's saturated blue (~0x3B82F6, variation 90) in the window's
lower half - the only blue there (the title sits in the upper third).
Verify the click landed by the blue vanishing (the progress view replaces
the button); retry up to 10 times.
…he button

Run 31446691812: every attempt logged 'blue text at 220, 330' - exactly
the 45%-height scan boundary, which lands inside the HERMES AGENT title
(title bottom ~47% of window height; button ~62%, measured from the run
2/5 recordings). The click-landed check then correctly reported no
advance, ten times. Raise the boundary to 55%, between the two.
…TALL_DIR on runners

Run 31447045981: the click landed (manifest received, stages ran) but
Stage-Uv failed with 'uv installed but not found at ...\bin\uv.exe'.
GitHub windows runners ship uv preinstalled WITH an astral install
receipt; astral's cargo-dist installer then updates the receipt's
location in place and ignores UV_INSTALL_DIR, so Install-Uv's managed
copy never appears. Seed HERMES_HOME\bin\uv.exe from the runner's uv
before launching the installer - Install-Uv short-circuits on an
existing managed uv, and 'user already has a managed uv' is a
legitimate install state, not a bypass.

Also abort the run the moment the tailed bootstrap log says
'bootstrap FAILED': the failure screen waits on a human Retry, and the
AHK helper would otherwise idle out its whole 25-minute marker
deadline (and its blue-text retry loop hammers the Retry button,
re-running doomed installs - observed in run 7).
… heuristic

Run 31447405319 is the big win and the bug in one log: uv seeding
worked, the Install click LANDED, and 6 stages ran (clone off the fake
remote via SSH rewrite, venv, all Python dependencies) - then the AHK
loop, still hunting for 'the button', PixelSearch-matched the PROGRESS
view's own blue stage text (left column, x~106-115), decided the UI
'did not advance' 10 times, threw, and the driver killed a healthy
install mid-node-deps.

Fixes, all sourced from that recording: narrow the scan band to the
center third so the left-column stage text can never match; verify a
click by the blue vanishing AT THE CLICK POINT (a 24px box) instead of
anywhere in the band; and never throw from the click loop - the
authoritative failure signal is the driver's 'bootstrap FAILED' log
abort, and the marker deadline caps a wedged UI.
… helper runs

The ImageSearch reference must be cropped from a lossless capture of the
real screen: the ffmpeg recording is H.264/yuv420p and its chroma
subsampling shifts glyph pixels enough that a video-sourced crop never
matches live rendering. Taken before the helper starts so no tooltip or
click marker contaminates it; lands in the log-dir artifact.
…Search removed

install-button.png is now cut from run 31449192962's LOSSLESS
welcome-screen.png (blue-text extents x465-558 y449-459 plus 3px margin,
verified complete '[ INSTALL ]' with no foreign pixels) instead of the
H.264 recording whose chroma subsampling made every video-sourced crop
miss the live screen. Tolerance drops from *60 to *20 accordingly.

PixelSearch is deleted outright: color-hunting matched the blue title
(run 31446691812) and the progress view's stage text (run 31447405319)
before it ever matched the button. Click-landed detection reuses the
same ImageSearch (button visible = not clicked). The TEMP
stop-after-screenshot exit is removed - the AHK path is live again.
…e the silent dep-install hang

The first full AHK run (31449642122) got all the way through install,
verify, and the desktop hand-off's git leg (fetch from the fake remote,
reset to target - the proxying works), then sat 65 minutes inside
'Updating Python dependencies' with zero uv output until the job
timeout cancelled it. Cancellation kills any chance of a post-mortem:
no process table, no partial log.

Run the hand-off through Start-Process with a driver-owned 45-minute
deadline (the same budget the staged-exe branch already gets), poll-tail
its log into the console, and on deadline dump the live uv/python/git
process table plus stderr tail before killing the tree - so a hang
diagnoses itself instead of burning another silent 75 minutes.
RUST_LOG=uv=debug is scoped to the update leg so uv says what it is
doing (or waiting on: cache lock, network, resolution).
…desktop updates

Two consecutive Windows E2E runs hung inside dependency install until
the job timeout: 31449642122 65 minutes in uv pip install .[all],
31453853006 43 minutes in the SQLite-repair uv sync (RUST_LOG=uv=debug
made THAT run hang earlier and with more stderr - the tell).

Root cause: scripts/desktop-update.ps1 redirects both child pipes but
only pumps stdout while the child runs; stderr is ReadToEnd()'d after
exit. uv and pip write progress to stderr. Once that pipe hits the
~64KB buffer, uv blocks on write, hermes update blocks on uv, the
hand-off blocks on hermes update: deadlock. Slower stderr producers
survive by finishing before the buffer fills, which is why the linux
sandbox never sees this.

Fix both sides of the class:
- managed_uv.py candidate sync + main.py _run_install_with_heartbeat:
  merge stderr into stdout (the pipe that IS drained). This arm heals
  EXISTING installs, whose old hand-off script drives the NEW python
  after the git reset.
- desktop-update.ps1: drain stderr concurrently via ReadToEndAsync so
  future bases never block regardless of what a child writes there.

_run_logged_subprocess and _run_npm_install_deterministic already
merge or capture both pipes; the two fixed sites were the only update-
path spawns that redirect stderr without draining it live.
…the deadlock it was diagnosing

Run 31457301901 proved both halves of the stderr fix and then hung
anyway, in uv pip install -e .[all] (process table caught it live):

- The SQLite-repair uv sync that deadlocked run 2 now streams its
  whole package list and completes in ~80s WITH debug tracing on -
  managed_uv.py is imported lazily after the git reset, so even this
  old base ran the fixed copy.
- The .[all] install runs _run_install_with_heartbeat from main.py,
  which was imported when hermes update STARTED - the v0.20.1 copy,
  which pipes uv's stderr undrained. RUST_LOG=uv=debug guaranteed
  >64KB of stderr, so the driver's own diagnostic manufactured the
  deadlock. Comments in both fixed call sites now state the real
  import-time reach of each arm.

This also explains run 1 failing WITHOUT tracing: the pipe budget is
cumulative across every child of the update sharing it. The old-code
sync burned ~30KB of it on the package list; the .[all] leg finished
the job. With the sync leg now on stdout, the old .[all] leg's
natural output should fit - which is the real-world story too: old
bases hang or survive on stderr luck, new bases are safe by
construction.
Every commit on main now proves, on a real Windows machine, that:
  1. the PRIOR commit (HEAD~1) installs from scratch through its own
     scripts/install.ps1 (-IncludeDesktop: uv, managed Python, Node,
     venv, packaged Electron Hermes.exe),
  2. that install updates TO this commit through the real Desktop GUI
     update path (scripts/desktop-update.ps1, the exact hand-off the
     Update button spawns -- fail-closed gates, marker lifecycle,
     hermes update, result JSON), and
  3. this commit updates FORWARD to a synthetic next commit, proving
     the updater code shipping in this commit is not the one that
     strands users when the next commit lands.

Staging: the driver bare-clones the checkout into serve.git and
redirects the canonical GitHub URLs at it with git insteadOf env
config, then advances the served main ref BASE -> CURRENT -> NEXT
between legs. Installer and updater run byte-for-byte unmodified.

Supersedes the AutoHotkey pixel-driving approach (NousResearch#68183): the GUI
Update button's entire effect is spawning desktop-update.ps1 with
documented flags, so driving that contract directly tests the same
production code deterministically.
The push-triggered validation run failed with zero jobs ('workflow file
issue'): job-level env only allows github/inputs/matrix/needs/secrets/
strategy/vars. Use a sibling of github.workspace for the E2E workroot
instead. actionlint now passes clean.
…tion

Will be reverted before merge; workflow_dispatch only becomes available
once the workflow file exists on the default branch.
…nfig

First CI run's install leg cloned real GitHub main instead of the staged
BASE (caught by the HEAD-at-BASE assert): install.ps1 sets
GIT_CONFIG_COUNT=1 / windows.appendAtomically itself, silently clobbering
the driver's env-config insteadOf rewrites. A driver-owned gitconfig file
selected via GIT_CONFIG_GLOBAL survives that (and install.ps1's own
--global writes land harmlessly in the same file). Verified locally by
cloning with the clobber vars set: clone lands on staged BASE.
The Windows E2E ran end-to-end green on this branch (run 31462244593):
install at BASE, update BASE->CURRENT, update CURRENT->NEXT, all asserts
passing. Back to main/nightly/tags/dispatch triggers only.
…I updater

Second job on the Windows E2E workflow covering the surfaces a user
actually touches, per Teknium's requirement:

* INSTALL: downloads the production Hermes-Setup.exe from
  hermes-assets.nousresearch.com, launches it HEADED, and AutoHotkey
  clicks Install -> waits -> clicks Launch (button templates + ImageSearch
  approach from @ethernet8023's NousResearch#68183, retargeted by process name and
  extended to exercise the Launch hand-off). The real Electron Hermes.exe
  window must appear.
* UPDATE x2: the installed Hermes.exe is launched under Playwright's
  Electron driver and the test CLICKS Settings -> About -> Update now.
  The production hand-off chain runs untouched: app quits, detached
  updater (repo script or staged binary) runs hermes update, rebuilds
  the desktop, relaunches Hermes.exe. Asserts: target sha, marker
  cleanup, result JSON when the script path wrote one, working hermes,
  and the RELAUNCHED app window. Leg 1 -> CURRENT, leg 2 -> synthetic
  NEXT.

Proof artifacts: per-step renderer screenshots (booted app, settings,
About panel, update-available, updating overlay), full-desktop frames
every 3s across the whole run, ahk.log, bootstrap-installer.log,
desktop-update-handoff.log — uploaded on success AND failure.

The website exe runs exactly as shipped (its own pinned install.ps1,
its baked release-pin commit); the only environmental deltas are the
serve.git URL redirect, uploadpack.allowAnySHA1InWant for the commit
pin fetch, and a placeholder provider key so the update legs meet the
app shell instead of onboarding.

The contract job from the previous commits is unchanged and independent
— it remains the rollback position if the GUI job proves flaky.
…rows

Frame-0005 of the proof capture showed the exact failure: 'Unhandled
error: (6) The handle is invalid' rendered over the installer within
seconds of launch. AutoHotkey started via Start-Process has no console,
so FileAppend to '*' (stdout) throws — and the throw fired inside Log(),
killing the script before it clicked anything. The installer then sat
untouched at the INSTALL screen for 50 minutes.

* Log() now try-wraps the stdout write (file log is the real record)
* Install/Launch clicks fall back to the button's relative window
  position when the NousResearch#68183-era PNG templates don't match the restyled
  UI ('[ INSTALL ]' bracket style visible in the same frame)
* install-finished has a second signal: 'bootstrap complete' in
  bootstrap-installer.log (read with write-sharing), so a template miss
  can't strand the wait
* driver passes the bootstrap log path as arg 3
…metry

Attempt 2's proof frames showed two bugs, both now fixed from the live
evidence:

1. The NousResearch#68183 install-button.png predated the installer UI restyle to the
   '[ INSTALL ]' bracket look, so the template never matched and we fell
   through to the position fallback. Re-captured install-button.png from a
   real CI desktop frame (the actual rendered button).

2. The fallback then clicked the WRONG spot: ahk_exe's first WinGetPos
   matched a hidden 16x16 helper window ('Window found at w=16 h=16' in
   ahk.log), and BTN_FY=0.87 aimed below the real button anyway. The
   button center measured at ~(0.50, 0.59) of the ~full-screen window.

Rewrite:
* WaitForRealWindow() skips phantom/hidden matches (requires w>400,h>300)
  and returns the true rect; the installer window is then activated before
  any click.
* Install-finished is now driven primarily by the authoritative
  'bootstrap complete' line in bootstrap-installer.log (matches
  BootstrapEvent::Complete), with the Launch template as a secondary
  signal and a window-relative fallback click.
* Fallback clicks use the corrected (0.50, 0.59) window fraction.
Attempt 3's proof frames showed the install SUCCEEDED end-to-end
(bootstrap complete, installer self-copied to HERMES_HOME) and the
window advanced to 'HERMES IS READY' with a [ LAUNCH ] button at the
same centered CTA spot the [ INSTALL ] button occupied — screen (511,454)
inside window x=64 y=34 w=896 h=659.

Two Launch-step bugs, both fixed from that evidence:
* launch-button.png was the stale NousResearch#68183 template and never matched the
  restyled '[ LAUNCH ]' button. Re-captured from the live frame.
* the window-relative fallback used fy=0.59, clicking y=422 — above the
  real button. Correct fraction is (454-34)/659 = 0.637. With the
  template now matching, the fallback is belt-and-braces anyway.

Install click, completion detection, and the app-window wait were all
already correct in attempt 3; only the Launch click missed.
The full GUI install flow now works end-to-end (attempt 4 proof: Install
clicked, bootstrap complete, Launch clicked, real Hermes.exe window
appeared 1024x720, installer exited, 5 Hermes processes running). The
only failure was an over-strict staging assertion.

The website Hermes-Setup.exe pins a main release commit. On a real
push-to-main run CURRENT is main's tip, so that pin is its ancestor and
the check holds. On a diverged feature branch CURRENT is a branch commit
the release pin is not an ancestor of — a legitimate topology, not a bug.
The update leg resets the checkout to serve.git's main ref (= CURRENT)
regardless of ancestry and asserts it lands there, which is the actual
forward-update proof. Downgrade the ancestor check to an informational
note so branch validation can exercise the update legs.
Windows PowerShell 5.1 reads .ps1 without a BOM under the legacy OEM
codepage, mis-decoding UTF-8 bytes. Em-dashes/box-drawing survived in
comments through attempts 2-4, but the previous commit added an em-dash
INSIDE a double-quoted Write-Host string — the misdecode there ate the
quote boundary and cascaded into a whole-file parse failure at the Stage
step ('Unexpected token', 'string is missing the terminator').

scripts/install.ps1 documents this exact constraint ('pure ASCII for PS
5.1 parser compatibility'). Strip all non-ASCII from the .ps1 and .ahk
files (em-dash->--, arrows->->, box-drawing->-). drive-update.cjs keeps
UTF-8 (Node decodes it natively). Both PowerShell files parse clean.
…heck

Install + GUI update leg now reached (attempt 6): full install passes,
first update leg begins. It tripped a preflight assert checking
apps/desktop/node_modules/@playwright/test — but the root npm ci HOISTS
workspace devDependencies to the REPO-ROOT node_modules, so that path is
empty by design. Node's own resolution walks up from apps/desktop and
finds it (which is exactly how the copied-in drive-update.cjs will load
it), so assert via 'node -e require.resolve(...)' from apps/desktop
instead of a hardcoded nested path.
…iver

Attempt 7 got the whole way into the GUI update leg: the installed
Electron app launched under Playwright, booted, composer attached, first
screenshot captured. It then couldn't find the settings gear -- the
ERROR screenshot showed why: a fresh install with no CONFIGURED provider
(the seeded .env key isn't read as model.provider) shows the onboarding
card ('Let''s get you setup with Hermes Agent'), which covers the shell
and its settings gear.

The update path needs no provider, so the driver now clicks 'I'll choose
a provider later' (with skip fallbacks) to dismiss onboarding and reach
the shell before looking for the gear. Harmless no-op when onboarding
isn't shown. Gear (aria-label 'Open settings') and About nav ('About')
selectors already match the real components.
…e event

Attempt 8 drove the ENTIRE GUI update click-path successfully: onboarding
dismissed, Settings opened, About opened, Update now clicked, updating
overlay shown. The hand-off log proves the real update then ran: desktop
(pid 8880) exited, venv unlocked, 'hermes update --yes --gateway --force
--branch main' fetched from serve.git, found 1 new commit, pulled, and
restored. Everything worked.

The only failure was the driver waiting on Playwright's app 'close'
event, which doesn't fire reliably when the Electron app self-quits for
the hand-off. Switch to the authoritative signal: poll for the
HERMES_HOME/.hermes-update-in-progress marker (or the result JSON, or a
genuine window-gone), which the hand-off writes ~4s after the click. The
PowerShell driver still owns asserting the OUTCOME (target sha, marker
cleanup, working hermes, relaunched app) after the driver returns.
teknium1 and others added 27 commits September 8, 2026 13:29
hideOnly chrome pinned the Sessions/Bots strip on at any tab count, so
never was a silent no-op. The panes stay; ⌘⌥T brings the strip back.

Co-authored-by: Cursor <cursoragent@cursor.com>
The importer stays in the command palette. The labeled nav row was
clutter next to New session / Capabilities / Messaging.

Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Lowering the session trigger must not replace the window-relative lean
selection budget with threshold times target_ratio. Invalidate the lean
cache through the existing property while preserving explicit legacy and
external-engine fallback behavior.

Narrow adaptation of the aux-sync diagnosis and invariants in NousResearch#93576,
without adding a required recalibration method to context engines.
Related: NousResearch#95681, NousResearch#93576

Co-authored-by: Turgut Kural <58116817+TurgutKural@users.noreply.github.com>
Add Move up/down controls for actual rooms without changing bot or folder
ordering. Preserve default pin/activity ordering until an explicit move,
retain hidden room slots, and persist Desktop-local order through room
updates, mirror merges, and hydration. No membership or routing writes.

Adapted narrowly from the group ordering idea in archived
NousResearch/Hermes-Bot-Mode#105 by @onuraycicek; rename already exists.

Co-authored-by: Onur Aycicek <onur.m.aycicek@gmail.com>
Port Adolanium's focused-turn pose from Hermes-Bot-Mode#101 and
hermes-agent#88134 to the current typed Bot Mode implementation.
Match the busy signal's connection-qualified focused owner rather than
the gateway socket, retain worker activity, and ease transitions in
elapsed time on the existing shared face clock.

Includes owner-isolation and animated-pose invariants, both proven red
on origin/main, and native Electron before/after verification against
a real temporary Hermes backend with held loopback inference.

Co-authored-by: Teknium <127238744+teknium1@users.noreply.github.com>
…tention-salvage

fix(bot-mode): clear resolved Group Chat attention and bind prompts across rename
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.