Commit graph

19665 commits

Author SHA1 Message Date
ethernet
b6c51d3a2b fix(ci): update actions/download-artifact to v8.0.1 2026-07-31 01:58:54 -04:00
ethernet
c6a7f9b0aa fix(ci): update actions/setup-node to 7.0.0 2026-07-31 01:58:49 -04:00
ethernet
e6343c4e92 fix(ci): update actions/cache to 6.1.0 2026-07-31 01:58:45 -04:00
ethernet
f84ec82d33 fix(ci): don't archive timings report
so we can view it directly in browser
2026-07-31 01:58:39 -04:00
ethernet
553db6ac63 fix(tests): forward HERMES_TEST_IMAGE through run_tests.sh hermetic env
- run_tests.sh: whitelist HERMES_TEST_IMAGE alongside the other
  HERMES_* passthroughs
2026-07-31 01:58:39 -04:00
ethernet
98134f4c20 refactor(tests): explicit HERMES_TEST_WORKERS over docker-suite auto-cap
Replace the runner's tests/docker auto-cap heuristic with explicit
width control:

- run_tests.sh forwards HERMES_TEST_WORKERS through its hermetic env -i
  (previously silently stripped — the documented override never worked
  through the wrapper)
- run_tests_parallel.py drops the _DOCKERD_BOUND_JOBS special-case; the
  suite-specific knowledge moves to the one place that runs that suite
- docker.yml pins HERMES_TEST_WORKERS=8. Width sweep with prewarmed
  image + split files: -j4 58-62s, -j8 39s, -j12 35s w/ ~2x per-file
  contention inflation; 8 is the knee.

Chain verified end-to-end: env var reaches the runner (6-worker probe),
no cap message on docker-only file lists, full suite 53/53 in 37.8s
at -j8.
2026-07-31 01:58:39 -04:00
ethernet
0744d23cfa perf(tests): split boot-heavy docker test files for parallel boots
The docker suite's wall time was max(whale files): four files each
serialized 2-3 ~110s container boots internally while 21 fast files
finished in seconds (P50 9.9s vs max 341s on the ARC runners). The
per-file parallel runner can only overlap what lives in separate files.

- test_dashboard.py -> 3 files (one boot each); shared _http_probe
  helper moves to conftest
- test_container_restart.py -> 2 files (restart_container fixture
  travels via the shared header; per-file container isolation is the
  point of the split)
- test_docker_exec_privilege_drop.py -> boot-heavy e2e login test split
  out; the two fast tests stay together
- test_config_migration.py: single test, unchanged

53 tests before and after, zero assertions changed — pure file
reorganization. Local (-j4, same cap as CI): 374.6s -> 58.0s wall,
slowest file 341.7s -> 18.6s.
2026-07-31 01:58:39 -04:00
ethernet
4e1eb2ec17 fix(ci): grant id-token to the docker.yml reusable-workflow call
docker.yml now requests id-token: write for WIF cache auth, but it is
invoked as a reusable workflow from ci.yml on PRs — and a called
workflow cannot request a permission its caller lacks. That mismatch
is a startup_failure (run 30596417013 died before any job). Add
id-token: write to ci.yml's top-level permissions.
2026-07-31 01:58:39 -04:00
ethernet
31c5021d72 ci: buildx cache in same-region Artifact Registry (keyless)
Swap type=gha buildx cache for type=registry against
us-central1-docker.pkg.dev/.../ci-cache — same region as the ARC
runners, so layer blobs stop round-tripping to GitHub's cache CDN on
every build/rerun.

Auth is keyless both ways: reads ride the runner pod's GKE Workload
Identity (no login needed for cache-from); writes exchange the
workflow's GitHub OIDC token via WIF (google-github-actions/auth,
fork-guarded — fork PRs build cache-cold exactly like type=gha).
publish keeps its own unconditional WIF auth (trusted contexts only).
Infra: hermes-agent-ci-infra ef8dfb2.
2026-07-31 01:58:39 -04:00
ethernet
da5fbf7f51 fix(tests): join agent-build threads; restore HERMES_TEST_IMAGE env
1. test_tui_gateway_server: the two _start_agent_build tests waited only
   for the _make_agent 'built' event, then popped the session while
   _build's tail was still running. The tail's session.info/error emit
   then landed on whatever _real_stdout a LATER test had patched in —
   the write_json concurrency test intermittently saw 9 lines instead
   of its own 8 (2-in-5 repro locally). Join the build thread (exposed
   as session['_agent_build_thread']) before popping. 10/10 clean
   full-file runs post-fix, was 2/5 failing.

2. docker.yml: the profile-action conversion dropped the step env —
   including HERMES_TEST_IMAGE, so all 25 per-file subprocesses each
   docker-built the image inside dind concurrently. That is the root
   cause of the 15-minute docker jobs and the teardown timeout storms.
   Restore it plus the blank-API-key policy vars.
2026-07-31 01:58:39 -04:00
ethernet
dac754954b fix(tests): pre-clean container names + tolerant teardown in docker conftest
Root cause of the last two amd64 docker failures: attempt 1 of a flaky
file times out mid-teardown (busy dind), the stale hermes-test-* name
survives, and the file-retry's docker run fails with a name Conflict —
so the retry mechanism itself was poisoned. The fixture now removes the
name BEFORE the test (fresh subprocess retry gets a clean slate), and
teardown swallows a slow-daemon TimeoutExpired instead of erroring a
passing test (1 passed, 1 error -> 1 passed).
2026-07-31 01:58:39 -04:00
ethernet
34c60b3313 fix(tests): auto-cap docker-suite workers in the runner itself
Move the dockerd-bound worker cap from a workflow env var into
run_tests_parallel.py: when every file in the run is under
tests/docker/, cap -j at 4 (the suite shares one docker daemon; width
beyond that thrashes it — files stretch ~100s -> ~900s and teardown
docker-rm calls blow their 10s timeout). Explicit -j or
HERMES_TEST_WORKERS always wins; mixed file lists are unaffected.
Verified: docker-only list caps 32->4, -j 12 respected, mixed list
uncapped. Drops the HERMES_TEST_WORKERS=4 pin from docker.yml.
2026-07-31 01:58:39 -04:00
ethernet
0a5682f1bd fix(ci): cap docker-suite workers at 4 — the suite is dockerd-bound
Diagnosis from the profiler + runner logs: with the cgroup-aware
default (-j 16 on the 8-CPU pods) all 25 files run concurrently against
the single dind daemon. Every file stretches to ~900s wall (P50 892s,
CPU-wall 14549s vs 918s wall) and teardown docker-rm calls exceed their
10s timeout — the job dies on teardown errors while tests themselves
pass 53/53. The arm64 lane (2 CPU → -j 4) went green for exactly this
reason. Pin the amd64 lane to the same effective width.
2026-07-31 01:58:39 -04:00
ethernet
a02db9a553 fix: doctor + termux-audio container-env parity
- doctor: 'inside a container' branch rewrote terminal_env to local for
  EVERY non-docker backend, so TERMINAL_ENV=vercel_sandbox diagnostics
  vanished when doctor ran inside a container (CI runner pods). Scope
  the informational skip to the implicit local case only; remote
  backends keep their real diagnostics. Fixes
  test_doctor_reports_vercel_backend_diagnostics on ARC runners — and
  for actual users running doctor in the Docker distribution with a
  remote terminal backend configured.
- termux audio test: detect_audio_environment() probes the real host
  for containment; pin is_container=False (a Termux device is never a
  container) so the containerized runner doesn't flip available=False.
2026-07-31 01:58:39 -04:00
ethernet
26778b7562 fix(tests): cgroup-aware worker count in parallel test runner
os.cpu_count() reports the HOST cores. In an ARC runner pod
(limit 8 CPU on a 22-core node) the runner spawned -j 44 workers on
8 usable CPUs — ~5x oversubscription. Every 'timing flake' family on
the self-hosted runners (docker rm teardown TimeoutExpired x111,
compression fork, termux probe, pty reaper, session hygiene) is CPU
starvation from that oversubscription, not real test bugs.

Read cgroup v2 cpu.max (v1 cfs_quota fallback) and clamp to host count.
Verified: --cpus=8 container reports 8, bare host unchanged.
HERMES_TEST_WORKERS override still wins.
2026-07-31 01:58:39 -04:00
ethernet
e4b38663dd fix(tests): two more container-env parity pins in gateway service tests
- test_supports_systemd_services_returns_true_when_systemctl_present:
  pin is_container=False (host contract; CI runner pods are containers)
- test_systemd_restart_gracefully_restarts_running_service_and_waits:
  stub _preflight_user_systemd — no user D-Bus in runner pods; the test
  asserts restart choreography, not D-Bus reachability
2026-07-31 01:58:39 -04:00
ethernet
6c100ec334 fix(tests): CI runner-pod env parity
Test fixes for ARC runner pods (containers) vs GHA ubuntu-latest VMs:

- test_gateway_wsl / test_copilot_acp_client: pin is_container=False on
  host-behavior tests — runner pods ARE containers and the prod code
  intentionally behaves differently there. Also drop inherited
  HERMES_REAL_HOME so nix dev shells don't leak into the assert.
- honcho memo + skill-utils external-dirs cache: add st_size to the
  mtime_ns cache keys. overlayfs (runner pods) coalesces rapid writes
  into one mtime tick, so same-tick edits were served stale (3 honcho
  pin tests + skill cache invalidation test).

Verified with KUBERNETES_SERVICE_HOST set to simulate the pod env.
2026-07-31 01:58:39 -04:00
ethernet
b48d73ab60 feat(ci): cache npm deps better
avoid reinstalling every time
2026-07-31 01:58:39 -04:00
ethernet
1fc0f09555 feat(ci): resource profiler 2026-07-31 01:58:39 -04:00
ethernet
d4e940f241 feat(ci): migrate all workflows to GKE self-hosted runners
Swap all `runs-on: ubuntu-latest` to `runs-on: arc-runner-set` all jobs.
The ARM docker build job in docker.yml uses `${{ matrix.runner }}`
and is left untouched since the GKE runner pool is x86_64 only.

Runners are backed by ARC (Actions Runner Controller) on a GKE cluster
with a spot preemptible node pool that scales based on job demand.

Use the baked Electron dependencies for the desktop E2E job.
2026-07-31 01:58:39 -04:00
brooklyn!
dbe14424ed
Merge pull request #75210 from NousResearch/bb/inline-attachments
TUI attachments live in the composer, not above the status bar
2026-07-30 23:56:57 -05:00
brooklyn!
cdca247424
Merge pull request #75180 from NousResearch/bb/composer-cut-placeholder
The placeholder comes back when you clear the composer
2026-07-30 23:54:19 -05:00
Brooklyn Nicholson
22af266b4f fix(tui): stop announcing attachments outside the composer
The token in the input line is the whole receipt. Drop the notices that
duplicated it somewhere the user was not looking: the drag-drop and
clipboard sys() lines, and the attachedImageNotice / "detected file: X"
activity rows above the status bar.

attachedImageNotice and imageTokenMeta have no callers left.
2026-07-30 23:42:58 -05:00
Brooklyn Nicholson
ca5ee5ed33 feat(tui): attach images inline at the cursor, delete the token to unattach
Every attach path now drops an `[[ Image N ]]` token where you are typing:
drag-drop, clipboard (bracketed and hotkey), /image, /paste. The composer
owns clipboard attach directly instead of calling back out to useMainApp.

Deleting the token is how you unattach — there is no second control.
updateInput is the one choke point every keystroke passes through, so
syncTokens reconciles there and detaches anything erased. That also fixes
a stale image riding along on the next unrelated turn.

Tokens and the input line get refs alongside state: paste-then-immediately
-Enter submits before React has re-rendered, and the submit path has to see
the token that was just added.
2026-07-30 23:42:53 -05:00
Brooklyn Nicholson
fead8c8d6a feat(tui): one token type for everything deferred in the composer
A collapsed paste and an attached image are the same idea: a `[[ … ]]`
marker sitting in the input line that stands in for a payload resolved at
submit. Model both as ComposerToken and give them one expander.

Image tokens resolve to nothing — the gateway already holds the file in
attached_images — so expandTokens eats an adjacent space to avoid leaving
a gap mid-sentence. nextImageIndex never reuses an index after a delete,
or two files would collide on one label.
2026-07-30 23:42:43 -05:00
Brooklyn Nicholson
0b4bd3c7c7 fix(desktop): the placeholder comes back when you clear the composer
Select-all + Cut emptied the text and left the composer blank — no draft,
no prompt. Delete had the same hole.

The placeholder is painted on `:empty`, and a cleared editor keeps a
scaffolding <br> so the contenteditable can't collapse to a sliver. Those
two facts collide: the moment the break lands the editor has a child,
`:empty` goes false, and the prompt never comes back.

CSS can't infer emptiness on its own either. A text node is invisible to
selectors, so `one<br>` and a lone `<br>` are the same shape — a structural
rule like `:has(> br:only-child)` paints the placeholder straight over the
user's text. The code that empties the editor is what knows, so it marks
the root and the condition reads `:is(:empty, [data-empty])`.

Both writers that reshape that root maintain the marker through one helper:
the normalizer, and renderComposerContents for a restored draft or an undo.
The message-edit composer shares the slot and the rule, so it takes the
same shared class instead of drifting on its own copy.

#74815 fixed the draft this stashed; the placeholder is a separate seam.
2026-07-30 22:54:20 -05:00
hermes-seaeye[bot]
b1858f33a1
fmt(js): npm run fix on merge (#75159)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2026-07-31 03:08:09 +00:00
brooklyn!
ab158e8088
Merge pull request #75127 from NousResearch/bb/close-last-tab
desktop: closing the last main tab lands on New session, and middle-click works on a real mouse
2026-07-30 21:58:43 -05:00
brooklyn!
9dd7ac670a
Merge pull request #75126 from NousResearch/bb/terminal-links
Open links clicked in the integrated terminal
2026-07-30 21:58:20 -05:00
Brooklyn Nicholson
193e5f84f7 fix(desktop): the main tab can be closed by gesture and menu
The tab strip decided the close gesture from the `uncloseable` flag, which the
workspace sets to keep its pane in the tree — so the one tab whose close now
does something couldn't be ⌘-clicked or middle-clicked, and its right-click
menu had no Close.

Read the gesture off the pane's registered closer instead, with the workspace
registering closeWorkspaceTab. An atom rather than a lookup, since that closer
comes from a wiring effect that lands after the strip's first paint.
2026-07-30 21:43:15 -05:00
Brooklyn Nicholson
c7b021ca48 fix(desktop): closing the last main tab lands on New session
The workspace pane can't leave the tree, so "close the main tab" only ever had
one answer wired: shift the next stacked session in. With main as the only tab
there was nothing to shift and ⌘W dead-ended on the tab the user was looking
at.

closeWorkspaceTab is now the one answer for every entry point — stacked
session still wins, and with nothing stacked main drops to a fresh New session
draft. A blank draft and a full-page view stay no-ops: a blank draft already
IS the post-close state.
2026-07-30 21:43:15 -05:00
Brooklyn Nicholson
463fbf5b16 fix(desktop): middle-click works on a real three-button mouse
Chromium on Windows and Linux answers a middle press inside a scroller by
starting the autoscroll pan, and the mouseup that ends the pan never becomes
an auxclick. Every surface carrying the gesture — tab strips, the session
list, the terminal rail — is a scroller, so middle-click only ever worked on
macOS, where autoscroll doesn't exist.

Arm on pointerdown, spend on the pointerup over the same element (press one
tab, release on another and nothing happens), and cancel the middle mousedown
on every press so the pan widget can't appear on a surface that owns the
button. One helper, four call sites.
2026-07-30 21:43:14 -05:00
Brooklyn Nicholson
4d6589c69c fix(desktop): stop ⌥-click spraying cursor escapes into the terminal
⌥-drag is the app's force-selection gesture over mouse-mode TUIs, but
xterm's default alt-click-moves-cursor claims the same click and emits one
cursor left/right escape per column of travel. Shells that don't consume
them echo the raw `^[[D` burst into the buffer. One gesture, one meaning.
2026-07-30 20:51:40 -05:00
Brooklyn Nicholson
0cec9896a1 fix(desktop): open links clicked in the integrated terminal
Both of xterm's link paths activate through `window.open()`, which the
window's setWindowOpenHandler denies, so ⌘-clicking a URL did nothing but
log "Opening link blocked as opener could not be cleared" — and the OSC 8
path fronted that dead end with a raw confirm() dialog. Route both through
the desktop bridge, the path every other external link in the app takes.

⌘-click on macOS, Ctrl-click elsewhere, matching VS Code's integrated
terminal, Terminal.app, and iTerm2. A bare click stays with the selection so
a misclick on a URL can't launch a browser.
2026-07-30 20:51:09 -05:00
Teknium
cc4cab2f59 chore: release v0.19.1 (2026.7.30) 2026-07-30 16:45:08 -07:00
Teknium
c0689c3bcb test(tui): make _load_enabled_toolsets assertions tolerant of first-release back-filled toolsets
The two exact-list assertions in test_tui_gateway_server froze the toolset
list and broke the moment _RECENTLY_SHIPPED_TOOLSETS back-filled bfl onto a
saved platform list — the exact behavior the sibling change ships on purpose.
Assert the invariant instead: the expected base set is present, and anything
extra must be inside _RECENTLY_SHIPPED_TOOLSETS (vacuously exact again once
that set empties between releases).
2026-07-30 16:34:08 -07:00
rob-maron
97c6a183af auto populate flux3 in tools for nous portal users 2026-07-30 16:34:08 -07:00
Teknium
524ab53994 fix(telegram): apply media read_timeout to all upload send paths, not just video
send_video got the 60s read_timeout but send_voice/send_audio/send_photo/
send_document/send_media_group/send_animation upload through the same PTB
request path and hit the same server-side processing wait before the
response arrives. Same class, all sites: they all pass
_MEDIA_SEND_READ_TIMEOUT now. Also drops an unused test helper.
2026-07-30 15:20:09 -07:00
rob-maron
0a2859cf9a drop env var 2026-07-30 15:20:09 -07:00
rob-maron
88f6949097 more conservative 2026-07-30 15:20:09 -07:00
rob-maron
061b04ebb4 fix video delivery 2026-07-30 15:20:09 -07:00
rob-maron
5932ec4552 more conservative to 120s 2026-07-30 15:20:09 -07:00
rob-maron
dcd7a95704 higher telegram media limits 2026-07-30 15:20:09 -07:00
rob-maron
4c7cc62f9f flux3 messaging system fixes 2026-07-30 15:20:09 -07:00
hermes-seaeye[bot]
5d6aae02bf
fmt(js): npm run fix on merge (#75055)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2026-07-30 22:06:42 +00:00
brooklyn!
3a2b332985
Merge pull request #74938 from NousResearch/bb/rail-own-worktree
fix(desktop): a session's coding rail follows its own worktree
2026-07-30 16:54:37 -05:00
rob-maron
4a798f4bce
improve polling for FLUX3 video gen (#75010)
* wait between polls
2026-07-30 16:29:27 -04:00
Andrew Fiebert
c9de69c6d5 fix(desktop): keep queued prompts bound to their origin session
ChatView migrated tip-keyed composer queue/draft entries onto queueSessionKey
whenever the two ids differed. queueSessionKey is route-driven and can flip to
Session B a frame before the store selection leaves Session A, so migrate
re-homed A queue entries onto B and the idle ChatBar auto-drained them into
the wrong chat.

Gate migrate on same-conversation lineage only (tip to root). Also honor
lineage when background queue drain decides selected/busy, so a root queue key
is not treated as idle/offscreen while the compression tip is still working.
2026-07-30 15:08:31 -04:00
rob-maron
07447bd5db
nous portal video gen (#74963)
Some checks are pending
CI / Detect affected areas (push) Waiting to run
CI / Python tests (push) Blocked by required conditions
CI / Python lints (push) Blocked by required conditions
CI / JS & TS checks (push) Blocked by required conditions
CI / Desktop E2E (push) Blocked by required conditions
CI / Docs Site (push) Blocked by required conditions
CI / Deny unrelated histories (push) Blocked by required conditions
CI / Check contributors (push) Blocked by required conditions
CI / Check uv.lock (push) Blocked by required conditions
CI / Check no committed infographics (push) Blocked by required conditions
CI / package-lock.json diff (push) Blocked by required conditions
CI / Lint Docker scripts (push) Blocked by required conditions
CI / Build&Test Docker image (push) Blocked by required conditions
CI / Supply-chain scan (push) Blocked by required conditions
CI / Review label gate (push) Blocked by required conditions
CI / OSV scan (push) Waiting to run
CI / CI review comment (live) (push) Blocked by required conditions
CI / All required checks pass (push) Blocked by required conditions
CI / CI timing report (push) Blocked by required conditions
Deploy Site / deploy-vercel (push) Waiting to run
Deploy Site / deploy-docs (push) Waiting to run
Docker Build, Test, and Publish / build (amd64, type=gha,scope=docker-amd64, type=gha,mode=max,scope=docker-amd64, linux/amd64, ubuntu-latest) (push) Waiting to run
Docker Build, Test, and Publish / build (arm64, type=gha,scope=docker-arm64, type=gha,mode=max,scope=docker-arm64, linux/arm64, ubuntu-24.04-arm) (push) Waiting to run
Docker Build, Test, and Publish / publish (amd64, type=gha,scope=docker-amd64, type=gha,mode=max,scope=docker-amd64, linux/amd64, ubuntu-latest) (push) Blocked by required conditions
Docker Build, Test, and Publish / publish (arm64, type=gha,scope=docker-arm64, type=gha,mode=max,scope=docker-arm64, linux/arm64, ubuntu-24.04-arm) (push) Blocked by required conditions
Docker Build, Test, and Publish / merge (push) Blocked by required conditions
auto-fix lint issues & formatting / Generate eslint --fix patch (push) Waiting to run
auto-fix lint issues & formatting / Apply patch (push) Blocked by required conditions
2026-07-30 14:52:15 -04:00
Brooklyn Nicholson
8f4ab7ad2c fix(desktop): coding rail reads only its own worktree
The row fell back to the global $repoStatus whenever repoPath was blank,
painting the main pane's branch and ± onto a tile whose cwd hadn't
resolved yet. The fallback bought nothing — the primary computed is keyed
to $currentCwd, which is empty in exactly that case — and cost a rail
showing a tree the session was never in.
2026-07-30 12:27:23 -05:00