fix(ci): guard nix-lockfile-fix workflow for upstream repo, bump actions - #3
Closed
waefrebeorn wants to merge 62 commits into
Closed
waefrebeorn wants to merge 62 commits into
waefrebeorn wants to merge 62 commits into
Conversation
* fix: update design system package, replace bg image, remove sync assets * fix(web): update bundled asset metadata * fix(web): normalize npm lockfile metadata * fix(nix): refresh npm lockfile hashes * chore(ci): trigger PR checks * fix(web): declare motion peer dependency * fix(nix): refresh npm lockfile hashes * chore(ci): trigger PR checks after dependency update * fix(web): restore cross-platform lockfile entries * fix(nix): refresh npm lockfile hashes * chore(ci): trigger PR checks after lockfile restore --------- Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
…ousResearch#28104) Bumps [webpack-dev-server](https://github.com/webpack/webpack-dev-server) from 5.2.3 to 5.2.4. - [Release notes](https://github.com/webpack/webpack-dev-server/releases) - [Changelog](https://github.com/webpack/webpack-dev-server/blob/main/CHANGELOG.md) - [Commits](webpack/webpack-dev-server@v5.2.3...v5.2.4) --- updated-dependencies: - dependency-name: webpack-dev-server dependency-version: 5.2.4 dependency-type: indirect ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Bumps [ws](https://github.com/websockets/ws) from 8.20.0 to 8.20.1. - [Release notes](https://github.com/websockets/ws/releases) - [Commits](websockets/ws@8.20.0...8.20.1) --- updated-dependencies: - dependency-name: ws dependency-version: 8.20.1 dependency-type: indirect ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
…search#24011) Bumps [mermaid](https://github.com/mermaid-js/mermaid) from 11.13.0 to 11.15.0. - [Release notes](https://github.com/mermaid-js/mermaid/releases) - [Commits](https://github.com/mermaid-js/mermaid/compare/mermaid@11.13.0...mermaid@11.15.0) --- updated-dependencies: - dependency-name: mermaid dependency-version: 11.15.0 dependency-type: indirect ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Extends the previous commit to cover the remaining additive-column index that sits on the same migration trap: - ``task_events.run_id`` -> ``idx_events_run`` was still in SCHEMA_SQL. A legacy ``task_events`` table predating NousResearch#17805 (no ``run_id``) would still abort ``executescript`` before ``_migrate_add_optional_columns`` could add the column. Hoisted out of SCHEMA_SQL and made unconditional in the migration alongside the other three indexes. - Removed the now-redundant ``CREATE INDEX idx_tasks_idempotency`` that was nested inside the ``if "idempotency_key" not in cols`` branch. The unconditional create lower in the function makes it idempotent on both fresh and legacy DBs. - Strengthened the regression test to cover all four indexes (``idx_tasks_session_id``, ``idx_tasks_tenant``, ``idx_tasks_idempotency``, ``idx_events_run``) and to seed a pre-NousResearch#17805 ``task_events`` shape that exercises the ``run_id`` migration path. The result: every ``CREATE INDEX`` that depends on an additive column now runs after the migration ensures the column exists. Verified against a realistic pre-NousResearch#16081 board fixture (tasks + task_events both legacy shape) — origin/main reproduces ``no such column: session_id``; this branch migrates cleanly and creates all four indexes.
When discord.py is not installed at import time, DISCORD_AVAILABLE=False and the view class definitions at module bottom are skipped. check_discord_requirements() performs a lazy install and sets DISCORD_AVAILABLE=True but never re-ran the class definitions, causing NameError on the first button interaction (exec approval, slash confirm, etc.). Extract the five ui.View subclasses into _define_discord_view_classes() and call it both at module load (when discord.py is pre-installed) and inside check_discord_requirements() after a successful lazy install.
…tory-truncation fix(tui): render full assistant text in scrollback (no history truncation)
For PR NousResearch#28774 (firecrawl integration tag). Co-authored-by: alt-glitch <balyan.sid@gmail.com>
…ser and web providers" (NousResearch#28862) This reverts commit 273ff5c.
…usResearch#26670) (NousResearch#26677) * fix(update): detect concurrent hermes.exe on Windows; retry + restart-defer quarantine Closes NousResearch#26670. When 'hermes update' runs on Windows with another hermes.exe alive (most commonly the Hermes Desktop Electron app's spawned backend) _quarantine_running_hermes_exe() fails to rename the venv shim with [WinError 32]. uv pip install -e . then exits 2, the git-pull fast path is silently abandoned, and the ZIP fallback runs (and fails the same way) before eventually succeeding. This change implements three of the five proposed fixes from the issue: 1. Concurrent-instance detection (preferred fix). _detect_concurrent_hermes_instances() uses psutil to enumerate processes whose .exe is one of our venv shims (hermes.exe / hermes-gateway.exe), excluding the caller's PID. When any match exists, cmd_update prints an actionable message naming the blocking PIDs and exits 2 BEFORE any destructive work. New --force flag bypasses the gate. 2. Retry + restart-deferred fallback. _quarantine_running_hermes_exe() now retries the rename up to 4 times with 100/250/500/1000 ms backoff (covers the transient AV-scanner-handle case). If all retries fail, it schedules the replacement via MoveFileExW with the OS deferred-rename flag so the new shim can land at the original path and the update completes; the old image is fully unloaded after the user's next system restart. 3. Actionable warning text. The old 'Could not quarantine: [WinError 32]' warning is replaced with one that names the likely culprits (Hermes Desktop, REPLs, gateway, AV) and points to the new --force flag. Tests: - 13 new tests in tests/hermes_cli/test_update_concurrent_quarantine.py covering: psutil-based enumeration, self-pid exclusion, case-insensitive matching of .EXE, no-psutil graceful degradation, off-Windows no-op, helpful warning formatting, retry-then-succeed, restart-deferred fallback, cmd_update abort + exit code 2, and --force bypass. - New autouse fixture in tests/hermes_cli/conftest.py defaults _detect_concurrent_hermes_instances to [] so the rest of the suite isn't tripped by the developer's own running hermes.exe. Opt-out marker 'real_concurrent_gate' registered in pyproject.toml. - Updating docs page (website/docs/getting-started/updating.md) gains a short section explaining the new Windows error and remediation. * chore: refresh uv.lock to match pyproject.toml exact pins aiohttp 3.13.4 -> 3.13.3 (matches pyproject pin: aiohttp==3.13.3) anthropic 0.87.0 -> 0.86.0 (matches pyproject pin: anthropic==0.86.0) hermes-agent 0.13.0 -> 0.14.0 (matches pyproject version) CI's uv lock --check was failing on the merged state because main drifted: pyproject.toml uses exact == pins for those two deps and the hermes-agent version was bumped to 0.14.0 but the lockfile still had 0.13.0.
Apply CREATE_NO_WINDOW flags when the cron scheduler launches job scripts on Windows so gateway-managed no-agent cron jobs do not flash cmd or python console windows every tick.
Apply Windows CREATE_NO_WINDOW flags to foreground local terminal subprocesses and tracked background processes so Hermes operations do not flash or steal focus with extra console windows.
Preserve Windows profile install decisions across UAC handoff, avoid visible console windows by launching via pythonw, make repeated install/start idempotent, recreate stale Scheduled Tasks, and separate start-now from login auto-start behavior. Add Windows gateway regression coverage and systemd setup tests for the shared install flow.
…-Windows Linux/macOS CI runners don't have ctypes.windll, so the elevated-gateway test fails at module load. Adding raising=False lets monkeypatch install the mock attribute without first requiring it to exist.
Introduces make_tool_result_message() in tool_dispatch_helpers.py as the single place where tool-result message dicts are built. All six construction sites in tool_executor.py, agent_runtime_helpers.py, and mini_swe_runner.py now use it, so tool_name is set in memory from the moment a message is created rather than relying on fallback logic in the flush paths. Fixes blank tool_name in both state.db and JSON session logs. Adds tests.
Adds a Termux runtime detection helper and gates three TUI defaults on it: - Skip the startup scrollback clear on Termux so users can review/copy earlier output after reopening the app. Desktop keeps the existing \x1b[2J\x1b[H\x1b[3J slate (AlternateScreen takes over there anyway). - Default INLINE_MODE on under Termux: primary-buffer rendering makes long-thread review and copy/paste much less fragile when users background/foreground the app. Override with HERMES_TUI_INLINE=0/1. - Default mouse tracking off under Termux so touch selection isn't intercepted by terminal mouse protocols. Explicit override via HERMES_TUI_MOUSE_TRACKING=0/1; legacy HERMES_TUI_DISABLE_MOUSE still works on desktop. Detection is purely env-based (TERMUX_VERSION or PREFIX path) with an explicit opt-out HERMES_TUI_TERMUX_MODE=0 for debugging. Non-Termux platforms keep every existing default. Co-authored-by: adybag14-cyber <252811164+adybag14-cyber@users.noreply.github.com>
…l-names-at-msg-construction fix blank tool_name entries in state.db and JSON session logs
Adds BrowseShSource — a new skill source adapter that integrates Browserbase's browse.sh catalog (169+ site-specific SKILL.md files) into the Hermes Skills Hub. - BrowseShSource class in tools/skills_hub.py implementing SkillSource ABC - Fetches browse.sh catalog API with 1h TTL cache - Full-text search across name, title, description, hostname, category, tags - fetch() downloads SKILL.md via sourceUrl (GitHub HTML -> raw URL conversion) - Registered in create_source_router() after LobeHubSource - Tests in tests/tools/test_skills_hub_browse_sh.py (7 tests, all passing)
- Add 'browse-sh' to _PER_SOURCE_LIMIT in both do_browse() and browse_skills() with limit=500 (covers full 171-skill catalog) - Add 'browse-sh' to --source argparse choices for both 'hermes skills browse' and 'hermes skills search' Without these, browse-sh fell back to the default cap of 50 results and was not filterable via --source.
The catalog's sourceUrl points at github.com/browserbase/browse.sh,
whose underlying repository is not always public — most raw URLs derived
from it 404. Use the per-skill detail endpoint instead, which returns a
skillMdUrl CDN blob that reliably resolves to the SKILL.md text. Fall
back to a raw.githubusercontent.com sourceUrl if the detail call fails.
- tools/skills_hub.py: rewrite BrowseShSource.fetch() to resolve via
/api/skills/{slug} -> skillMdUrl; drop the unreachable _to_raw_url
helper; expose the resolved URL in bundle.metadata.skill_md_url.
- tests/tools/test_skills_hub_browse_sh.py: match the real catalog
shape (name = task name, slug = host/task-id), exercise the
detail-endpoint -> blob two-call flow, and add a fallback test.
- scripts/release.py: map kylejeong21@gmail.com -> Kylejeong2.
Add browse.sh (browse-sh) to the supported-sources table and integrated-hubs section in user-guide/features/skills.md, and to the --source notes in reference/cli-commands.md. Companion to the BrowseShSource adapter merged in NousResearch#28936.
Resync the setup wizard's in-memory config after the shared model picker writes to disk so the wizard's final save does not overwrite auxiliary choices or other provider updates.\n\nAdds a regression test for auxiliary task choices saved by the picker.
…for base_url trust (NousResearch#27132) When config.yaml has provider: ollama (or vllm/llamacpp/llama-cpp) with a non-loopback base_url, auth.py's resolve_provider() correctly normalises the alias to 'custom' at the top level, but two sites in runtime_provider.py were still comparing the *original* string against the literal 'custom': - _config_base_url_trustworthy_for_bare_custom() rejected non-loopback URLs because cfg_provider_norm was 'ollama', not 'custom'. - _resolve_openrouter_runtime() only entered the trust branch when requested_norm == 'custom'. Both sites now consult resolve_provider() and treat any alias that resolves to 'custom' identically. Result: provider: ollama + LAN IP no longer silently falls through to OpenRouter (HTTP 401), matching the behaviour of provider: custom with the same base_url. E2E verified across 6 cases (ollama/vllm/llamacpp/custom + LAN; ollama + loopback; openrouter + cloud) — all route to the configured endpoint; 'frobnicate' + LAN still rejects with AuthError as before. Also adds scripts/release.py AUTHOR_MAP entry for @stepanov1975 (PR NousResearch#22074 — wizard config picker preservation, cherry-picked into the preceding commit).
…ousResearch#28864) `cli.py` was eager-importing `openai._base_client` at module-load time purely to monkeypatch `AsyncHttpxClientWrapper.__del__` (defense against "Press ENTER to continue..." errors when AsyncOpenAI clients are GC'd against dead event loops). That import cost ~166ms / ~30MB on every cold CLI start because openai's type tree (responses/*, graders/*) is huge. Replace with a `sys.meta_path` finder that intercepts the first import of `openai._base_client` from anywhere in the codebase, lets the normal load run, then applies the `__del__ = lambda self: None` patch before control returns to the caller. Same correctness guarantee (patch applies before any AsyncOpenAI instance can be constructed), zero cost until the SDK is actually needed. Hot path: every hermes chat / gateway boot / cron tick / subagent spawn. A/B benchmark, 10 runs each, fresh subprocess: BEFORE AFTER delta import cli wall 0.86s 0.62s -28% (median) import cli wall 0.85s 0.59s -31% (min) import cli RSS 91.2MB 74.0MB -19% (median) The `neuter_async_httpx_del` function in agent/auxiliary_client.py is unchanged; its tests still pass and any future callers can still invoke it directly. Verified: - import cli no longer pulls openai into sys.modules - first 'from openai._base_client import AsyncHttpxClientWrapper' triggers the patch; __del__.__name__ == '<lambda>' - tests/run_agent/test_async_httpx_del_neuter.py: 9/9 pass - tests/agent/test_auxiliary_client.py: 159/159 pass - tests/cli/: 715/715 pass
…rgeted hot-path optimizations (NousResearch#28866) * perf(config): add load_config_readonly() fast path for hot agent loop `load_config()` is called from the agent loop's per-API-call hot path via `get_provider_request_timeout()` and `get_provider_stale_timeout()` — both invoked once per turn from `_resolved_api_call_timeout()` in run_agent.py. Profiling a synthetic 20-tool-call agent run revealed: - 21 invocations of `load_config()` cumulating 56ms (~17% of agent loop) - 34,398 deepcopy calls totaling 37ms (config defensive deepcopy + chain) - 8,652 `_expand_env_vars` invocations (~412 per turn) Microbench (cache-hit, real config.yaml present): load_config() 265us/call (125us deepcopy + 140us infra) load_config_readonly() 138us/call (~48% faster) `load_config_readonly()` returns the cached dict directly without the defensive deepcopy. Documented contract: caller must not mutate. Returns plain dict (not MappingProxyType) so downstream `isinstance(x, dict)` guards keep working — caught during initial implementation when MappingProxyType broke get_provider_request_timeout's guard logic. Wired into hermes_cli/timeouts.py (the two functions called per agent turn). load_config() is unchanged for the 263 other call sites that mutate the result before save_config(), are not in the hot path, or where the safety guarantee matters more than the perf. Profile A/B (cached config, 21-turn agent loop): BEFORE AFTER delta get_provider_request_timeout 55ms 16ms -71% total function calls 399k 160k -60% deepcopy calls (in hotspots) 34,398 ~0 ~elim Verified: - isinstance(load_config_readonly(), dict) is True - timeout/stale resolutions correct - load_config() still returns isolated mutable deepcopies - tests/hermes_cli/test_config*.py / test_timeouts.py: 102/102 pass - tests/cli/ + tests/agent/test_auxiliary_client.py: 883/883 pass * perf(redact): substring pre-screens skip non-matching regex chains Every log record passes through `RedactingFormatter.format` which calls `redact_sensitive_text`, which historically ran ALL 13 secret-pattern regexes against every line — including DB connection strings, JWTs, Discord mentions, Signal phone numbers, etc. — even for typical clean log records like 'INFO run_agent: API call completed'. Add cheap substring pre-checks before each regex pass. False positives still run the regex (which then matches nothing); false negatives are impossible because every pattern requires the gated substring to match its leading anchor: - `_PREFIX_RE` gated on any of 33 known credential prefix substrings - `_ENV_ASSIGN_RE` gated on `=` in text - `_JSON_FIELD_RE` gated on `:` and `"` in text - `_AUTH_HEADER_RE` gated on `uthorization`/`UTHORIZATION` in text - `_TELEGRAM_RE` gated on `:` in text - `_PRIVATE_KEY_RE` gated on `BEGIN` and `-----` - `_DB_CONNSTR_RE` gated on `://` in text - `_JWT_RE` gated on `eyJ` in text - URL userinfo/query gated on `://` - `_redact_form_body` gated on `&` and `=` - `_DISCORD_MENTION_RE` gated on `<@` - `_SIGNAL_PHONE_RE` gated on `+` Microbench (5 typical log records, 20k iterations each): BEFORE AFTER delta redact_sensitive_text per call 5.63us 1.79us -68% Real-world impact: ~244 log records emitted in a 30-turn agent loop, so the chain saves ~1ms of CPU per conversation. Bigger win is the reduction in regex execution and GC pressure during heavy logging sessions (verbose logging, gateway message processing). Security regression test: 30 secret-containing inputs (sk-/ghp_/JWT/DB connstr/Auth-Bearer/private key/URL userinfo/Discord/Signal/etc.) verified to produce identical redacted output before/after. All 75 existing tests/agent/test_redact.py cases pass. The `?access_token=foo&code=bar` (bare query string, no scheme) case that 'leaks' is pre-existing behavior — the URL query redaction requires a well-formed URL with scheme+host. Not a regression. * perf(run_agent): cache _needs_thinking_reasoning_pad result per (provider, model, base_url) Profile of a 31-turn synthetic agent run shows `_needs_thinking_reasoning_pad` fires 495 times (~16 per turn) and each call ran 3 helper methods, each hitting `base_url_host_matches` 1-4 times via `urlparse`. Total cost: 3,342 base_url_host_matches calls + 3,373 urlparse calls accounting for ~36ms of agent-loop overhead (~7% of the entire post-network work). Provider / model / base_url don't change during a conversation except via `switch_model` and fallback activation — both of which already overwrite those attributes atomically. Cache the result on a tuple key; since the key is derived from the very fields that would change, the cache auto-invalidates on the next read after a switch. No manual invalidation needed in switch_model / _try_activate_fallback. Profile A/B (31-turn cached-config agent run): BEFORE AFTER delta _needs_thinking_reasoning_pad cum 18ms 1ms -94% _copy_reasoning_content_for_api cum 17ms 1ms -94% base_url_host_matches calls 3,342 372 -89% urlparse calls 3,373 403 -88% total function calls 296k 223k -25% Verified: - tests/run_agent/test_deepseek_reasoning_content_echo.py: 36/36 pass - tests/run_agent/ (full): 1383/1383 pass + 3 skipped
Bumps [ws](https://github.com/websockets/ws) from 8.20.0 to 8.20.1. - [Release notes](https://github.com/websockets/ws/releases) - [Commits](websockets/ws@8.20.0...8.20.1) --- updated-dependencies: - dependency-name: ws dependency-version: 8.20.1 dependency-type: indirect ... Signed-off-by: dependabot[bot] <support@github.com>
…Hub API (NousResearch#28943) The Windows installer fetched the latest git-for-windows release via api.github.com/repos/git-for-windows/git/releases/latest, which is rate-limited to 60 requests/hour/IP for unauthenticated callers. Users behind CGNAT, corporate NAT, dorm WiFi, or shared ISP routinely hit the limit, and the installer aborts asking them to install Git manually. Switch to a pinned release tag (v2.54.0.windows.1) and a static github.com/.../releases/download/<tag>/<asset> URL. Static download URLs are served by GitHub's blob storage and are not subject to the API rate limit. Trade-offs: - We have to bump the pin when we want a newer Git for Windows. The installer doesn't depend on Git features beyond 'works', so this is a once-a-year maintenance cost at most. - Loses the (cosmetic) MB size display, since we no longer have asset metadata. Replaced with the version string in the 'Downloading ...' line instead.
waefrebeorn
pushed a commit
that referenced
this pull request
Jul 2, 2026
…y prefixed Companion to the NousResearchGH-25255 incoming-strip fix from @hayka-pacha. Without this, build_anthropic_kwargs unconditionally added 'mcp_' to every tool name in step 3, so a native MCP server tool registered as 'mcp_composio_X' was sent as 'mcp_mcp_composio_X' on the wire. The incoming strip only removes ONE prefix, which still worked on first call, but on subsequent calls the model pattern-matched the single-prefixed form from message history and produced names that stripped to 'composio_X' — registry miss, dispatch fail. The history-rewrite block (#4) already has this guard. Apply the same guard to the schema-rewrite block (#3) so round-trip is symmetric. Added 4 outgoing-side tests. Existing 7 incoming-side tests still pass. Author map: hayka-pacha added for PR NousResearch#25270 salvage attribution. Refs NousResearchGH-25255.
waefrebeorn
pushed a commit
that referenced
this pull request
Jul 2, 2026
## Summary - Forwards chat-completions `timeout` into the Codex Responses stream call. - Adds total elapsed-time enforcement while the Responses stream is still yielding events. - Closes the underlying client on timeout to unblock stalled streams, then raises `TimeoutError`. - Adds focused tests for timeout forwarding and total timeout enforcement. ## Why The Codex auxiliary adapter can be used by non-interactive auxiliary work such as context compression. If the stream keeps yielding progress-like events but never completes, SDK socket/read timeouts do not necessarily protect the full operation. This makes the CLI look stuck until the user force-interrupts the whole session. This is a refreshed upstream-ready version of the earlier fork fix around `d3f08e9a0` / PR #3. ## Verification - `python -m py_compile agent/auxiliary_client.py tests/agent/test_auxiliary_client.py` - `python -m pytest -o addopts='' tests/agent/test_auxiliary_client.py::TestCodexAuxiliaryAdapterTimeout -q` - `git diff --check`
waefrebeorn
pushed a commit
that referenced
this pull request
Jul 2, 2026
1. browser_tool.py: Replace **args spread on browser_click, browser_type, and browser_scroll handlers with explicit parameter extraction. The **args pattern passed all dict keys as keyword arguments, causing TypeError if the LLM sent unexpected parameters. Now extracts only the expected params (ref, text, direction) with safe defaults. 2. fuzzy_match.py: Update module docstring to match actual strategy order in code. Block anchor was listed as #3 but is actually NousResearch#7. Multi-occurrence is not a separate strategy but a flag. Updated count from 9 to 8.
waefrebeorn
pushed a commit
that referenced
this pull request
Jul 2, 2026
…lls/ - Rename skill to touchdesigner-mcp (matches blender-mcp convention) - Move from skills/creative/ to optional-skills/creative/ - Fix duplicate pitfall numbering (#3 appeared twice) - Update SKILL.md cross-references for renumbered pitfalls - Update setup.sh path for new directory location
waefrebeorn
pushed a commit
that referenced
this pull request
Jul 2, 2026
… OAuth gates
Two parallel public-path allowlists drifted: _PUBLIC_API_PATHS in
hermes_cli/web_server.py (legacy _SESSION_TOKEN middleware) and
_GATE_PUBLIC_PREFIXES in hermes_cli/dashboard_auth/middleware.py
(OAuth gate). The legacy list included /api/status (documented as a
non-sensitive read-only liveness target); the OAuth gate's list did not.
Effect: every wildcard-subdomain agent surfaced as STARTING/down to the
portal even though the dashboard was serving correctly. Nous account
service (src/server/agents/fly-provider.ts
getInstanceRuntimeStatus) fetches ``/api/status`` without a cookie
as its sole liveness probe; the OAuth gate's 401 looked identical to
'agent dead' on the portal side.
Fix: lift the allowlist into hermes_cli/dashboard_auth/public_paths.py
and have both middlewares import it. _path_is_public now consults
the shared frozenset first, then falls back to the gate's
auth-bootstrap/static prefix list. Future additions to the public list
hit both gates automatically.
Endpoint inventory (verified safe to remain public):
* /api/status — version, gateway state, active session count,
auth-gate shape. Portal liveness probe target.
* /api/config/defaults — config-defaults feed for the SPA's Config page
* /api/config/schema — config schema for the SPA's Config page
* /api/model/info — model catalogue metadata (context windows)
* /api/dashboard/themes — theme manifests for the skin engine
* /api/dashboard/plugins — plugin manifests for the dashboard
No user data, no session content, no secrets. Same shape an external
monitoring agent would hit on /healthz.
Tests:
* New: test_gated_status_is_public (regression guard with the NAS
fly-provider.ts liveness-probe rationale spelled out in the docstring)
* New: test_other_public_api_paths_are_public_under_gate (parametrised
over the rest of PUBLIC_API_PATHS — proves 401 / 302-to-login is
never the response)
* New: docker integration check #3 in
test_dashboard_oauth_gate_engaged_by_default — /api/status
remains 200 under the gate AND reports auth_required=True so the
portal can distinguish modes
* Updated: test_full_login_round_trip_unlocks_gated_api now probes
/api/sessions instead of /api/status (status is public, so it
can no longer distinguish 'logged in' from 'gate accidentally
disabled')
* Updated: TestApi401Envelope (the no-cookie / invalid-cookie /
dead-cookie tests) probes /api/sessions for the same reason
* Updated: docker integration check #2 in
test_dashboard_oauth_gate_engaged_by_default probes
/api/sessions to prove the gate is intercepting
* Removed: dead _login() helper in
test_dashboard_auth_status_endpoint.py (no longer needed since
/api/status is reachable cold)
Companion to docs/handover/hermes-agent-dashboard-s6-insecure-fix.md
(the --insecure flag fix that shipped earlier).
waefrebeorn
pushed a commit
that referenced
this pull request
Jul 2, 2026
…hain probe (NousResearch#34340) * fix(codex): surface error code in Responses 'failed' status errors When a Codex Responses turn ends with status=failed, the response carries the failure details under `response.error` as `{code, message, param, ...}`. The previous extractor pulled only `message`, so users seeing a rate-limit failure got a bare "Slow down" string indistinguishable from a generic stream truncation; an internal_error with empty message degraded to a dict dump ("{'code': 'internal_error', 'message': ''}"). Extract a `_format_responses_error()` helper that: - prefixes `code` when both code and message are present (e.g. 'rate_limit_exceeded: Slow down') - falls back to the bare `code` when message is empty - accepts both dict and attribute-style payloads (SDK and JSON-RPC paths) - preserves the prior status-only fallback when no error payload exists Apply the same helper at the sibling site in `codex_app_server_session.run_turn()` so codex-CLI subprocess turn failures get the same treatment. Tests: - 8 new unit tests for `_format_responses_error` covering both shapes, empty/missing fields, non-string fields, and the status-only fallback. - 2 regression tests on `_normalize_codex_response` for failed status with and without a code, asserting the exact RuntimeError message. - All 3603 tests in tests/agent/ pass. Adapted from anomalyco/opencode#28757. * feat(prompt): universal task-completion guidance + local Python toolchain probe Two cross-model failure modes get a single-line answer in the cached system prompt. Both gated by config (default on), both add zero overhead when not needed, both verified via real AIAgent prompt builds. ## What changed `TASK_COMPLETION_GUIDANCE` — short prompt block applied to ALL models. Targets two failure modes observed on a real Sarasota real-estate build task: (1) Opus stopped after writing an 85-byte stub and gave a prose response with finish_reason=stop on call #3 of 90; (2) DeepSeek pushed through a PEP-668 wall, then returned fabricated listings instead of admitting the blocker. Both behaviors are model-family-agnostic, so the guidance lives outside the existing tool_use_enforcement gate (~192 tokens, paid once per session via prefix cache). `tools/env_probe.py` — local Python toolchain probe. Detects python3/pip/uv/PEP-668 state and emits ONE short line in the system prompt when something is non-default. Emits NOTHING when the env is clean (zero token cost for normal users). Skipped entirely for remote terminal backends (docker/modal/ssh) — they have their own probe. Example output on a broken environment (the actual case): Python toolchain: python3=3.11.15 (no pip module), python=missing (use python3), pip→python3.12 (mismatch), PEP 668=yes (use venv or uv). ## Config Both flags live under `agent.` in config.yaml, default True: agent: task_completion_guidance: true # universal "finish the job" block environment_probe: true # local Python toolchain hints Neither addition required a `_config_version` bump — deep-merge fills defaults in for existing user configs. ## Validation | Test surface | Result | |---|---| | tests/tools/test_env_probe.py | 10/10 pass (probe unit) | | tests/run_agent/test_run_agent.py — new classes | 8/8 pass (integration) | | TestToolUseEnforcementConfig | 17/17 pass (no regression) | | TestBuildSystemPrompt | 9/9 pass (no regression) | | TestInvalidateSystemPrompt | 2/2 pass (no regression) | | tests/agent/test_prompt_builder.py | 124/124 pass (no regression) | | tests/hermes_cli/ | 5662/5662 pass (config defaults) | | E2E AIAgent build (broken env) | Both blocks present, 2,178 chars | | E2E AIAgent build (clean env) | 771-char net overhead, env probe silent |
waefrebeorn
pushed a commit
that referenced
this pull request
Jul 2, 2026
A bare `/resume` printed the recent-sessions list but armed no selection state, so typing just `3` on the next line was sent to the agent as chat instead of resuming session #3. `/resume 3` worked, but the natural list-then-pick flow did not. Arm a one-shot pending-resume prompt when bare `/resume` shows the list, and consume the next bare numeric input as the selection (out-of-range is reported, non-numeric/other commands disarm it). Resolves against the same _list_recent_sessions(limit=10) list used everywhere else. Closes NousResearch#34584.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What does this PR do?
Guards the nix-lockfile-fix CI workflow to only run on the upstream repository (prevents fork failures), and bumps GitHub Actions versions for Node.js 24 compatibility.
Related Issue
None — CI maintenance.
Type of Change
Changes Made
.github/workflows/nix-lockfile-fix.yml— addsgithub.repository == 'NousResearch/hermes-agent'guard, bumps checkout action to v4, setup-node to v4How to Test
Trigger the workflow on a fork — it should skip instead of failing.
Checklist