Repository navigation
sync(upstream): merge 74ad422d50 batch - #264
Conversation
…dirs Profile-spawned workers (kanban bots, cron jobs) can inherit a PATH of only version-manager dirs — observed in the wild as one nvm node dir repeated 7x. The uv-installed browser-use binary is a POSIX sh trampoline that resolves dirname/realpath through PATH, so it died with 'realpath: not found … exec: /python: not found' (exit 127) before its own Python ever started. _base_subprocess_env now floors the child PATH via browser_tool's _merge_browser_path (the agent-browser backend already guards the same hazard), degrading to appending FHS bin dirs if that import is ever unavailable. Windows is a no-op (.cmd shims don't trampoline). Verified: unit tests + real uvx browser-use --version under a nvm-only-PATH worker env, rc 127 -> rc 0.
npm install inside install_node_deps() builds native addons (e.g. node-pty) via node-gyp, which needs a C/C++ compiler. That was never checked, so a missing g++ only surfaced as a generic "npm install failed or timed out" deep inside npm's own output — and because install_node_deps failing short-circuits the rest of main() via `|| return`, users end up with no `hermes` command and no clue why.
Reapply the Playwright dependency update from NousResearch#77773 and regenerate package-lock.json against current main. This fixes the Chromium installation hang under Node 26. Fixes NousResearch#76312 Supersedes NousResearch#77773
Salvage follow-up for NousResearch#88993: the preflight was defined but never called from the prerequisites stage or the full-install path, so the Fedora node-gyp failure (NousResearch#93063) would still occur. Wire it in after check_node in both sequences.
tests/stress/ was dead weight: its own conftest set collect_ignore_glob = ["*.py"], so pytest has never collected a single file from it, the advertised --run-stress flag was a permanent no-op, and no CI workflow ever invoked the scripts (NousResearch#93135). Rather than wire a nightly lane for scripts that were never verified end-to-end, remove the suite. Kanban concurrency behavior remains covered by the regular tests under tests/hermes_cli/ and tests/gateway/. Closes NousResearch#93135.
…havior tests The salvaged hardening tests matched main.ts source text to assert that fetchConnectionStatus reaches for a bearer and that Apply preflights before persisting. A rename breaks them while a real auth regression that keeps the substrings passes. Make the preflight a first-class option on applyConnectionConfigAtomically so its ordering is observable, and assert it through the seam: preflight runs before either write, and a rejected preflight leaves both stores and the activation untouched.
…nce (NousResearch#93197) The showAllProfiles browse-mode flag is persisted to localStorage, but every restart it was force-collapsed anyway: initializeConnectionsRegistry restores the last-used source via selectConnection, and selectConnection's post-activation path unconditionally ran $showAllProfiles.set(false). That collapse is correct for a user click on the connection picker (a concrete-source action), but the silent boot restore is not a user action. Gate both reset sites on pendingTarget === null && activeConnectionId === null (the fresh-boot state) so the persisted preference survives restart, while any user-initiated switch still collapses browse mode. Regression tests cover both directions: boot restore preserves true, a user switch collapses it. Fixes NousResearch#93197
…ocal migrateV1ToRegistry runs exactly once, only when connections.json is absent. A user who was local at that moment and pointed Settings -> Gateway at a remote afterwards gets a live remote the registry cannot name: the descriptor resolves to no connectionId, primary still says 'local', and the boot-time launch pick force-switches the window onto a fresh local backend seconds after the sessions list paints. That backend has no provider, so onboarding pops. Reconcile on read: when the v1 global route names a remote with no matching registry entry, register it and adopt it as primary/last-used, then persist so the repair happens once. Narrow on purpose — an already-registered route is left alone even when primary names something else, because that is the user's pick in the Connections panel, not drift. Replaces the hand-edit-connections.json workaround users have been trading.
Reconciliation repairs the drift at its source, but it can still fail to persist (read-only or full userData), which leaves a window live on a source the registry cannot name. $activeConnectionId is null there, the preferred-id guard misses, and the restore re-homes a working connection. Return early when a connection is live but unnameable. The registry has no claim on a source it does not know about.
The toggle was gated on having 2+ registered sources, which hid it in exactly the local-only state the drift produces — the state where a user most needs to change what launch restores.
Gemini 3 resolves JSON-Schema $ref/$defs pointers inside a
functionResponse.response payload and rejects unknown references with
HTTP 400 INVALID_ARGUMENT ('referenced name #/$defs/...' does not match
a display_name; see vercel/ai#14369).
tool_describe (and any tool whose result is itself a JSON Schema) returns
schema text that previously went back as a structured response, tripping
Gemini's pointer resolution. Detect such results with a $ref-pointer scan
and wrap them as opaque text instead.
Adds regression tests for the wrap path and the unchanged structured path.
…-positive tolerance Address review feedback: - Add tests for deeply-nested $ref (recursion), top-level JSON array (already wrapped, no 400 path), and $ref without '#/' prefix (stays structured). - Document the deliberate structural (false-positive-tolerant) detection and its O(n) cost in the helper docstring.
…g the breaker Fixes NousResearch#93022. A short session (protection window >= transcript) hits the "insufficient messages" / "no compressible window" branches twice and permanently trips the anti-thrash breaker, even though nothing was eligible to compress - compression was never attempted, so there is nothing "ineffective" to score. The session then rides past the threshold with no compaction possible (recovery probes only soften, not fix, the misclassification). Distinguish "nothing eligible right now" from "attempted and underperformed": - New transient _structural_no_op_backoff_until (in-memory, 300s) armed by _record_structural_no_op() at the three structural no-op sites: insufficient_messages, no_compressible_window, empty_post_handoff_window. No strikes accumulate; auto-compaction resumes on its own once the backoff lapses or the transcript outgrows the protection window. - The backoff gates should_compress via _automatic_compression_blocked_locally and surfaces in _compression_block_reason as "structural_backoff:<seconds>". - NousResearch#40803's frozen-CLI guarantee is preserved: a transcript that can never shrink retries at most once per backoff window instead of every turn. - force=True (/compress) clears an active backoff before attempting; record_completed_compaction() lifts it - both prove the transcript is compressible/being worked. - Genuine attempted-but-underperformed verdicts still strike the durable ineffective counter unchanged. Tests: new tests/agent/test_context_compressor_structural_backoff.py; updated the two tests that asserted the old strike-on-noop behavior.
…ousResearch#93022) Two suites still encoded the pre-NousResearch#93093 contract that the three structural no-op branches (insufficient_messages, no_compressible_window, empty_post_handoff_window) increment _ineffective_compression_count: - tests/agent/test_compaction_anti_thrash.py:: TestMinimumMessagesBranch::test_too_few_messages_records_an_ineffective_pass - tests/run_agent/test_infinite_compaction_loop.py:: TestCompressNoOpRegistersIneffective::{test_no_op_increments_counter, test_two_no_ops_block_should_compress} Structural no-ops are transcript-shape facts, not evidence of an incompressible floor, so they now arm _structural_no_op_backoff_until and leave the strike counter untouched. Update the tests to pin the new contract (count unchanged, backoff armed via time.monotonic(), should_compress blocked while it holds) and rename accordingly. The outcome contract of test_two_no_ops_block_should_compress is preserved: repeated no-ops still block further automatic compression.
…files Running `pytest tests/agent/test_prompt_builder.py tests/agent/test_system_prompt.py` failed test_build_system_prompt_records_stable_prefix with AttributeError: '...SimpleNamespace' object has no attribute '_emit_status' (NousResearch#93018). A truncation warning recorded by test_prompt_builder.py stays in the shared thread context under plain pytest, so the later file's build_system_prompt call drains a warning and forwards it to agent._emit_status - which the test stub lacked. Harden both sides: - tests/agent/test_system_prompt.py: _make_agent() stub gains a no-op _emit_status, so draining a stray warning is harmless. - tests/agent/test_prompt_builder.py: autouse fixture drains pending truncation warnings after every test, leaving the ContextVar clean. The order-dependent failure no longer reproduces in either ordering.
…uilder test Follow-up to the ContextVar-leak fix: the autouse fixture now drains on both sides (drain(); yield; drain()) so earlier files can't pollute this file's assertions either.
…under minimal parent PATH Exercises the real build_subprocess_env()/_resolve_hermes_bin_dir chain (no helper mocks) under a simulated systemd/cron minimal PATH, the exact call path cron/scheduler._run_job_script uses. Companion to NousResearch#93082.
The CUA spawn-env tests froze PATH == '/usr/bin:/bin' verbatim. _sanitize_subprocess_env now (intentionally) prepends the hermes console-script dir for all sanitized children (NousResearch#92998), so these assertions flip to the contract: original entries preserved as suffix, hermes bin dir first when prepended.
…port ConvertTo-LongPath short-circuits for ordinary long paths (no ~\d alias), so $script:LastResolver is only assigned when a short path actually needs expansion. The ResolvedPathReport block read it unconditionally, which is fatal under Set-StrictMode before any install stage runs (NousResearch#93017: fresh installs died at line 367 through three different invocation styles). Initialize it to 'none' — the resolver's own value for "nothing ran" — at script scope before Set-LongProfileEnvVars can invoke a resolver.
…short-circuits The ordinary-long-path early return now records why no resolver ran, so the ResolvedPathReport stays truthful instead of silently inheriting a stale value from an earlier call in the same session. Diagnostics hunk taken from PR NousResearch#93100. Co-authored-by: aniruddhaadak80 <[email protected]>
|
Important Review skippedAuto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro Plus Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
૮ >ﻌ< ა ci reviewran on cf7cdd5 — merge: sync batumi/live #273 heartbeat supervision into sync
|
| Package | Before | After |
|---|---|---|
| @playwright/test | 1.58.2 |
1.62.1 |
| nanostores | 1.4.0 |
1.4.2 |
| playwright | 1.58.2 |
1.62.1 |
| playwright-core | 1.58.2 |
1.62.1 |
| ➕ @babel/code-frame | — | 8.0.0 |
| ➕ @babel/compat-data | — | 8.0.0 |
| ➕ @babel/core | — | 8.0.1 |
| ➕ semver (nested under @babel/core) | — | 7.8.5 |
| ➕ @babel/generator | — | 8.0.0 |
| ➕ @babel/helper-compilation-targets | — | 8.0.0 |
| ➕ semver (nested under @babel/helper-compilation-targets) | — | 7.8.5 |
| ➕ @babel/helper-globals | — | 8.0.0 |
| ➕ @babel/helper-string-parser | — | 8.0.0 |
| ➕ @babel/helper-validator-identifier | — | 8.0.4 |
| ➕ @babel/helper-validator-option | — | 8.0.0 |
| ➕ @babel/helpers | — | 8.0.0 |
| ➕ @babel/parser | — | 8.0.4 |
| ➕ @babel/template | — | 8.0.0 |
| ➕ @babel/traverse | — | 8.0.4 |
| ➕ @babel/types | — | 8.0.4 |
| ➕ @rolldown/plugin-babel | — | 0.2.3 |
| ➕ blobatar | — | 2.0.0 |
| ➕ js-tokens | — | 10.0.0 |
| ➕ lru-cache | — | 11.5.2 |
| ➕ @types/babel__core | — | 7.20.5 |
| ➕ @types/babel__generator | — | 7.27.0 |
| ➕ @types/babel__template | — | 7.4.4 |
| ➕ @types/babel__traverse | — | 7.28.0 |
| ➕ @types/gensync | — | 1.0.5 |
| ➕ @types/jsesc | — | 2.5.1 |
| ➕ babel-plugin-react-compiler | — | 1.0.0 |
| ➕ driver.js | — | 1.8.0 |
| ➕ empathic | — | 2.0.1 |
| ➕ @babel/code-frame | — | 8.0.0 |
| ➕ @babel/compat-data | — | 8.0.0 |
| ➕ @babel/core | — | 8.0.1 |
| ➕ @babel/generator | — | 8.0.0 |
| ➕ @babel/helper-compilation-targets | — | 8.0.0 |
| ➕ @babel/helper-globals | — | 8.0.0 |
| ➕ @babel/helper-string-parser | — | 8.0.0 |
| ➕ @babel/helper-validator-identifier | — | 8.0.4 |
| ➕ @babel/helper-validator-option | — | 8.0.0 |
| ➕ @babel/helpers | — | 8.0.0 |
| ➕ @babel/parser | — | 8.0.4 |
| ➕ @babel/template | — | 8.0.0 |
| ➕ @babel/traverse | — | 8.0.4 |
| ➕ @babel/types | — | 8.0.4 |
| ➕ @rolldown/plugin-babel | — | 0.2.3 |
| ➕ js-tokens | — | 10.0.0 |
| ➕ lru-cache | — | 11.5.2 |
| ➕ semver | — | 7.8.5 |
How to fix:
Add the ci-reviewed label after verifying the version changes are expected.
ℹ️ Info
CI-sensitive file review · View job
PR touches sensitive files, but the ci-reviewed label has been added, approving them.
Sensitive files changed:
.github/actions/detect-changes/action.yml.github/workflows/docker.yml.github/workflows/e2e-desktop.yml.github/workflows/label-rerun.yml.github/workflows/nix.yml.github/workflows/rust-tests.yml.github/workflows/tests-os.yml.github/workflows/windows-venv-e2e.yml
debug info
CI timings
CI timings · View report · View job
Wall time 9m34s vs 12m14s (-21.8%). 7 job(s) slower, 4 faster, 1 unchanged.
- Docs Site / docs-site-checks: -113.0s
- Python tests / e2e: -13.0s
- Lint Docker scripts / Lint Dockerfile (hadolint): +11.0s
- Python lints / Windows footguns (blocking): +8.0s
- OSV scan / Scan lockfiles / osv-scan: -7.0s
Upstream commit 69abe41 (fix(desktop): re-home backend after profile rename) introduced this author; attribution check requires a mapping file. GitHub login verified via commit author association.
Upstream renamed ci.yml -> ci.yaml (merged in this batch); the ownership manifest must track the new path so fork delta classification keeps CI workflow changes owned.
Upstream 693c0e1 removed both case-colliding mapping files (they cannot coexist on case-insensitive filesystems). Our attribution gate scans the full upstream range of this sync batch, so the capitalized variant appeared unmapped. Restore just the capitalized mapping (the commit e6f59e5 author variant present in the scanned range) — a single file, no case collision — and own both spellings in the fork-patch-governance manifest.
[email protected] -> OutThisLife, [email protected] -> pierrenode, [email protected] -> rroverin, [email protected] -> TomSpoct. Logins verified via GitHub commit-author associations on upstream commits in this sync range.
The fork has no larger-runner allocation: ubuntu-latest-96-core, ubuntu-latest-32-core and windows-latest-32-core jobs queue forever. Swap all 8 occurrences to ubuntu-latest / windows-latest (free for public repos). Jobs run slower but complete.
The 74ad422 merge resolution of agent/relay_runtime.py dropped the fork's logical_llm_contexts field, _run_context_bounded helper, the context= parameter on _close_scope_handle, and the context-aware _finish_logical_calls path that relay_llm.py still calls — 28 tests failed with AttributeError. .github/workflows/ci.yml (fork-owned patch file) was also dropped, breaking tests/scripts/test_ci_workflow.py. Both restored from batumi/live onto the upstream-newer file.
The 74ad422 merge dropped RelaySessionCoordinator.finalize_conversation_async (present on batumi/live); test_relay_shared_metrics_runtime.py async-barrier tests fail with AttributeError. Restored from batumi/live.
…nish helpers) RelaySessionCoordinator.finalize_conversation_async calls host.close_session_async, which was also dropped in the 74ad422 merge. Restored close_session_async with _begin_close_session/ _finish_close_session split from batumi/live, adapted to the upstream-newer operation-held lifecycle (operation_already_held=True).
… sites The 74ad422 merge adopted upstream's guardrail_warning field in _managed_values (6-tuple) but left four sequential-path call sites (read_preview, drive_preview, annotate_preview, tour et al.) unpacking 5 values — ValueError: too many values to unpack (expected 5).
The merge combined upstream's run_in_session wrapper with the fork's
logical-context design: logical_context.run(run_in_session, ...) re-copies
the session context over the logical one, so the native pop cannot see
the logical scope handle ('not found: scope handle not found') and the
handle leaks (turn.logical_llm_calls != {}). Restore batumi/live's direct
logical_context.run(pop_relay_scope, ...) form.
The merge adopted upstream's 'skip Relay for concurrent turns' guard (relay_enabled=False on 2nd turn), which breaks the fork's concurrent-turn design: overlapping logical calls each need their own turn scope. Restore batumi/live semantics: - active-turn registry admits concurrent turns (had_active_turn gate only defers segment rotation to an idle boundary) - turn scope pushed in a dedicated turn.context via _run_context_bounded - RelayTurnContext.context field restored - end_turn closes the turn scope with context=turn.context
…rm dropped) The 74ad422 merge took upstream's single-32-core-job js-tests.yml, which (a) breaks the fork contract test expecting the discovery+matrix form with two cooldown-protected npm installs, and (b) is tuned for a paid 32-core runner. The fork's matrix form spreads checks across free 4-core runners.
Both ci.yaml (upstream rename of ci.yml) and ci.yml (restored fork file) existed at HEAD with workflow name CI and identical triggers and concurrency group - two live workflows named CI cancelled each other mid-run (3 consecutive cancelled dispatch runs with zero failing jobs). Keep the fork's ci.yml, drop the upstream ci.yaml.
The merge replaced the fork's 12-slice matrix tests.yml with upstream's single-job form, dropping the slice_count workflow input — ci.yml's 'slice_count: 12' then caused workflow startup_failure. Restore the fork's sliced form (parallel free-runner friendly), keeping HERMES_TEST_WORKERS: 4 per slice.
Upstream's test reads .github/workflows/ci.yaml; the fork keeps the workflow at ci.yml. The contract itself passes against ci.yml: every needs.detect.outputs.* reference is declared.
…-labels The restored fork ci.yml (and the contract test now pointing at it) join the free-runner migration patch family after the ci.yaml removal.
Resolve gateway/run.py in favor of batumi/live: #273 replaced the inline loop-heartbeat block with _start_loop_heartbeat_supervised() (supervised early start + idempotent revival). CI on the PR head is green (run 32906328252).
Upstream sync batch (merge-commit landing, #237 pattern)
Merges
upstream/mainthrough 74ad422 intobatumi/livevia a true merge commit so upstream ancestry is recorded (keepscheck_fork_deltaon the direct path).Method
e88d8831d2(sync(upstream): merge e88d8831d2 batch #245) via-s ours→ 18 conflicts, all resolved semantically (relay bridge-loop reuse + contextvars scopes, sync-bridge helpers over upstream session-close lifecycle, systemd restart shortcut kept, watchdog hard-deadline + upstream probes, upstream terminal-job guard over locked cron ledger, background-review test updated for run-token prepare lock).upstream-base→74ad422d50; provenancesource_head/squash_commit= this branch head line (19aecf2d36/1a2703642d).scripts/check_fork_delta.py --fetch-provenance-source: PASS.Acceptance
Merge with 'Create a merge commit' (not squash) to preserve ancestry.