Skip to content

feat: allow custom endpoints to use responses api - #1041

Closed
mxyhi wants to merge 1 commit into
NousResearch:mainfrom
mxyhi:feat/custom-responses-api-mode
Closed

feat: allow custom endpoints to use responses api#1041
mxyhi wants to merge 1 commit into
NousResearch:mainfrom
mxyhi:feat/custom-responses-api-mode

Conversation

@mxyhi

@mxyhi mxyhi commented Mar 12, 2026

Copy link
Copy Markdown
Contributor

Summary

  • allow custom OpenAI-compatible endpoints to opt into codex_responses via config or HERMES_API_MODE
  • keep provider resolution and API key behavior unchanged while exposing /v1/responses for custom endpoints
  • fix request debug dumps to show /responses when the active API mode is codex_responses
  • add coverage for runtime provider api-mode overrides and responses-mode debug dump URLs

Validation

  • /Users/langhuam/workspace/self/hermes-agent/venv/bin/pytest tests/test_runtime_provider_resolution.py tests/test_run_agent_codex_responses.py -q
  • HERMES_DUMP_REQUESTS=1 HERMES_DUMP_REQUEST_STDOUT=1 hermes chat -Q -q "Reply with exactly OK."
  • verified runtime request dump uses http://127.0.0.1:9208/v1/responses with a custom endpoint configured in ~/.hermes/config.yaml

@teknium1

Copy link
Copy Markdown
Contributor

Is there a reason for this support - what models have responses api support 🤔

@mxyhi

mxyhi commented Mar 15, 2026

Copy link
Copy Markdown
Contributor Author

Is there a reason for this support - what models have responses api support 🤔
gpt-5.4,gpt-5.3-codex

@mxyhi

mxyhi commented Mar 15, 2026

Copy link
Copy Markdown
Contributor Author

Is there a reason for this support - what models have responses api support 🤔

https://ai-sdk.dev/cookbook/guides/openai-responses

@mxyhi
mxyhi force-pushed the feat/custom-responses-api-mode branch from 9d22a09 to 6bb62eb Compare March 15, 2026 01:45
teknium1 pushed a commit that referenced this pull request Mar 17, 2026
Add HERMES_API_MODE env var and model.api_mode config field to let
custom OpenAI-compatible endpoints opt into codex_responses mode
without requiring the OpenAI Codex OAuth provider path.

- _get_configured_api_mode() reads HERMES_API_MODE env (precedence)
  then model.api_mode from config.yaml; validates against whitelist
- Applied in both _resolve_openrouter_runtime() and
  _resolve_named_custom_runtime() (original PR only covered openrouter)
- Fix _dump_api_request_debug() to show /responses URL when in
  codex_responses mode instead of always showing /chat/completions
- Tests for config override, env override, invalid values, named
  custom providers, and debug dump URL for both API modes

Inspired by PR #1041 by @mxyhi.
teknium1 added a commit that referenced this pull request Mar 17, 2026
…de (#1651)

Add HERMES_API_MODE env var and model.api_mode config field to let
custom OpenAI-compatible endpoints opt into codex_responses mode
without requiring the OpenAI Codex OAuth provider path.

- _get_configured_api_mode() reads HERMES_API_MODE env (precedence)
  then model.api_mode from config.yaml; validates against whitelist
- Applied in both _resolve_openrouter_runtime() and
  _resolve_named_custom_runtime() (original PR only covered openrouter)
- Fix _dump_api_request_debug() to show /responses URL when in
  codex_responses mode instead of always showing /chat/completions
- Tests for config override, env override, invalid values, named
  custom providers, and debug dump URL for both API modes

Inspired by PR #1041 by @mxyhi.

Co-authored-by: mxyhi <mxyhi@users.noreply.github.com>
@teknium1

Copy link
Copy Markdown
Contributor

Merged via PR #1651. Reimplemented your feature onto current main (282 commits ahead) with authorship preserved.

Improvements in the reimplementation:

  • Also applies api_mode override to named custom providers (_resolve_named_custom_runtime), not just the openrouter fallback path
  • Additional test coverage for invalid values and named providers

Thanks for the contribution, @mxyhi!

teknium1 pushed a commit that referenced this pull request Apr 22, 2026
…imeout

Upgrades agent-browser from 0.13.0 to 0.26.0, picking up 13 releases of
daemon reliability fixes:

- Daemon hang on Linux from waitpid(-1) race in SIGCHLD handler (#1098)
- Chrome killed after ~10s idle due to PR_SET_PDEATHSIG thread tracking (#1157)
- Orphaned Chrome processes via process-group kill on shutdown (#1137)
- Stale daemon after upgrade via .version sidecar and auto-restart (#1134)
- Idle timeout not firing (sleep future recreated each loop) (#1110)
- Navigation hanging on lifecycle events that never fire (#1059, #1092)
- CDP attach hang on Chrome 144+ (#1133)
- Windows daemon TCP bind with Hyper-V port conflicts (#1041)
- Shadow DOM traversal in accessibility tree snapshots
- doctor command for user self-diagnosis

Also wires AGENT_BROWSER_IDLE_TIMEOUT_MS into the browser subprocess
environment so the daemon self-terminates after our configured inactivity
timeout (default 300s). This is the daemon-side counterpart to the
Python-side inactivity reaper — the daemon kills itself and its Chrome
children when no commands arrive, preventing orphan accumulation even
when the Python process dies without running atexit handlers.

Addresses #7343 (daemon socket hangs, shadow DOM) and #13793 (orphan
accumulation from force-killed sessions).
angelburgosrosado pushed a commit to angelburgosrosado/hermes-agent that referenced this pull request Apr 27, 2026
…de (NousResearch#1651)

Add HERMES_API_MODE env var and model.api_mode config field to let
custom OpenAI-compatible endpoints opt into codex_responses mode
without requiring the OpenAI Codex OAuth provider path.

- _get_configured_api_mode() reads HERMES_API_MODE env (precedence)
  then model.api_mode from config.yaml; validates against whitelist
- Applied in both _resolve_openrouter_runtime() and
  _resolve_named_custom_runtime() (original PR only covered openrouter)
- Fix _dump_api_request_debug() to show /responses URL when in
  codex_responses mode instead of always showing /chat/completions
- Tests for config override, env override, invalid values, named
  custom providers, and debug dump URL for both API modes

Inspired by PR NousResearch#1041 by @mxyhi.

Co-authored-by: mxyhi <mxyhi@users.noreply.github.com>
aj-nt pushed a commit to aj-nt/hermes-agent that referenced this pull request May 1, 2026
…imeout

Upgrades agent-browser from 0.13.0 to 0.26.0, picking up 13 releases of
daemon reliability fixes:

- Daemon hang on Linux from waitpid(-1) race in SIGCHLD handler (NousResearch#1098)
- Chrome killed after ~10s idle due to PR_SET_PDEATHSIG thread tracking (NousResearch#1157)
- Orphaned Chrome processes via process-group kill on shutdown (NousResearch#1137)
- Stale daemon after upgrade via .version sidecar and auto-restart (NousResearch#1134)
- Idle timeout not firing (sleep future recreated each loop) (NousResearch#1110)
- Navigation hanging on lifecycle events that never fire (NousResearch#1059, NousResearch#1092)
- CDP attach hang on Chrome 144+ (NousResearch#1133)
- Windows daemon TCP bind with Hyper-V port conflicts (NousResearch#1041)
- Shadow DOM traversal in accessibility tree snapshots
- doctor command for user self-diagnosis

Also wires AGENT_BROWSER_IDLE_TIMEOUT_MS into the browser subprocess
environment so the daemon self-terminates after our configured inactivity
timeout (default 300s). This is the daemon-side counterpart to the
Python-side inactivity reaper — the daemon kills itself and its Chrome
children when no commands arrive, preventing orphan accumulation even
when the Python process dies without running atexit handlers.

Addresses NousResearch#7343 (daemon socket hangs, shadow DOM) and NousResearch#13793 (orphan
accumulation from force-killed sessions).
02356abc pushed a commit to 02356abc/hermes-agent that referenced this pull request May 14, 2026
…de (NousResearch#1651)

Add HERMES_API_MODE env var and model.api_mode config field to let
custom OpenAI-compatible endpoints opt into codex_responses mode
without requiring the OpenAI Codex OAuth provider path.

- _get_configured_api_mode() reads HERMES_API_MODE env (precedence)
  then model.api_mode from config.yaml; validates against whitelist
- Applied in both _resolve_openrouter_runtime() and
  _resolve_named_custom_runtime() (original PR only covered openrouter)
- Fix _dump_api_request_debug() to show /responses URL when in
  codex_responses mode instead of always showing /chat/completions
- Tests for config override, env override, invalid values, named
  custom providers, and debug dump URL for both API modes

Inspired by PR NousResearch#1041 by @mxyhi.

Co-authored-by: mxyhi <mxyhi@users.noreply.github.com>
02356abc pushed a commit to 02356abc/hermes-agent that referenced this pull request May 14, 2026
…imeout

Upgrades agent-browser from 0.13.0 to 0.26.0, picking up 13 releases of
daemon reliability fixes:

- Daemon hang on Linux from waitpid(-1) race in SIGCHLD handler (NousResearch#1098)
- Chrome killed after ~10s idle due to PR_SET_PDEATHSIG thread tracking (NousResearch#1157)
- Orphaned Chrome processes via process-group kill on shutdown (NousResearch#1137)
- Stale daemon after upgrade via .version sidecar and auto-restart (NousResearch#1134)
- Idle timeout not firing (sleep future recreated each loop) (NousResearch#1110)
- Navigation hanging on lifecycle events that never fire (NousResearch#1059, NousResearch#1092)
- CDP attach hang on Chrome 144+ (NousResearch#1133)
- Windows daemon TCP bind with Hyper-V port conflicts (NousResearch#1041)
- Shadow DOM traversal in accessibility tree snapshots
- doctor command for user self-diagnosis

Also wires AGENT_BROWSER_IDLE_TIMEOUT_MS into the browser subprocess
environment so the daemon self-terminates after our configured inactivity
timeout (default 300s). This is the daemon-side counterpart to the
Python-side inactivity reaper — the daemon kills itself and its Chrome
children when no commands arrive, preventing orphan accumulation even
when the Python process dies without running atexit handlers.

Addresses NousResearch#7343 (daemon socket hangs, shadow DOM) and NousResearch#13793 (orphan
accumulation from force-killed sessions).
gweeteve pushed a commit to gweeteve/hermes-agent that referenced this pull request Jun 2, 2026
…de (NousResearch#1651)

Add HERMES_API_MODE env var and model.api_mode config field to let
custom OpenAI-compatible endpoints opt into codex_responses mode
without requiring the OpenAI Codex OAuth provider path.

- _get_configured_api_mode() reads HERMES_API_MODE env (precedence)
  then model.api_mode from config.yaml; validates against whitelist
- Applied in both _resolve_openrouter_runtime() and
  _resolve_named_custom_runtime() (original PR only covered openrouter)
- Fix _dump_api_request_debug() to show /responses URL when in
  codex_responses mode instead of always showing /chat/completions
- Tests for config override, env override, invalid values, named
  custom providers, and debug dump URL for both API modes

Inspired by PR NousResearch#1041 by @mxyhi.

Co-authored-by: mxyhi <mxyhi@users.noreply.github.com>
gweeteve pushed a commit to gweeteve/hermes-agent that referenced this pull request Jun 2, 2026
…imeout

Upgrades agent-browser from 0.13.0 to 0.26.0, picking up 13 releases of
daemon reliability fixes:

- Daemon hang on Linux from waitpid(-1) race in SIGCHLD handler (NousResearch#1098)
- Chrome killed after ~10s idle due to PR_SET_PDEATHSIG thread tracking (NousResearch#1157)
- Orphaned Chrome processes via process-group kill on shutdown (NousResearch#1137)
- Stale daemon after upgrade via .version sidecar and auto-restart (NousResearch#1134)
- Idle timeout not firing (sleep future recreated each loop) (NousResearch#1110)
- Navigation hanging on lifecycle events that never fire (NousResearch#1059, NousResearch#1092)
- CDP attach hang on Chrome 144+ (NousResearch#1133)
- Windows daemon TCP bind with Hyper-V port conflicts (NousResearch#1041)
- Shadow DOM traversal in accessibility tree snapshots
- doctor command for user self-diagnosis

Also wires AGENT_BROWSER_IDLE_TIMEOUT_MS into the browser subprocess
environment so the daemon self-terminates after our configured inactivity
timeout (default 300s). This is the daemon-side counterpart to the
Python-side inactivity reaper — the daemon kills itself and its Chrome
children when no commands arrive, preventing orphan accumulation even
when the Python process dies without running atexit handlers.

Addresses NousResearch#7343 (daemon socket hangs, shadow DOM) and NousResearch#13793 (orphan
accumulation from force-killed sessions).
waefrebeorn pushed a commit to waefrebeorn/slermes that referenced this pull request Jul 2, 2026
…imeout

Upgrades agent-browser from 0.13.0 to 0.26.0, picking up 13 releases of
daemon reliability fixes:

- Daemon hang on Linux from waitpid(-1) race in SIGCHLD handler (NousResearch#1098)
- Chrome killed after ~10s idle due to PR_SET_PDEATHSIG thread tracking (NousResearch#1157)
- Orphaned Chrome processes via process-group kill on shutdown (NousResearch#1137)
- Stale daemon after upgrade via .version sidecar and auto-restart (NousResearch#1134)
- Idle timeout not firing (sleep future recreated each loop) (NousResearch#1110)
- Navigation hanging on lifecycle events that never fire (NousResearch#1059, NousResearch#1092)
- CDP attach hang on Chrome 144+ (NousResearch#1133)
- Windows daemon TCP bind with Hyper-V port conflicts (NousResearch#1041)
- Shadow DOM traversal in accessibility tree snapshots
- doctor command for user self-diagnosis

Also wires AGENT_BROWSER_IDLE_TIMEOUT_MS into the browser subprocess
environment so the daemon self-terminates after our configured inactivity
timeout (default 300s). This is the daemon-side counterpart to the
Python-side inactivity reaper — the daemon kills itself and its Chrome
children when no commands arrive, preventing orphan accumulation even
when the Python process dies without running atexit handlers.

Addresses NousResearch#7343 (daemon socket hangs, shadow DOM) and NousResearch#13793 (orphan
accumulation from force-killed sessions).
LiteSoul added a commit to LiteSoul/hermes-agent that referenced this pull request Jul 20, 2026
When Hermes is force-killed (taskkill, power loss, app close) the
agent-browser daemon on Windows holds its deterministic TCP port as
an orphan. The next Hermes session's first browser command hits
EADDRINUSE and surfaces as ``"Daemon failed to start (port: ...)"`` —
the browser tool becomes completely unusable until the user finds and
kills the zombie node.exe in Task Manager.

Reproduced live on Windows: ``browser_navigate`` returned
``"Daemon failed to start (port: 127.0.0.1:55478)"`` with a zombie
``daemon.js`` (PID 39924) holding the port. After killing the zombie,
``browser_navigate`` succeeded. See PR NousResearch#65701 for full transcript.

## What this iteration does (per teknium1's review)

Two scoped changes only:

1. **Retry on daemon-start failure** — when ``_run_browser_command``
   detects ``"Daemon failed to start"``, it synchronously runs
   ``_reap_orphaned_browser_sessions`` (kills Hermes-managed
   zombies only), resets the session so the retry gets a fresh
   ``h_<uuid>`` port, and retries the command exactly once.
   ``_daemon_retried`` parameter prevents recursion beyond one attempt.
   The retry path only fires *after* the daemon has failed to start —
   there is no active browser session to lose; a daemon that can't
   bind its port never opened Chromium.

2. **Dropped the app-directory sweep entirely** — the original
   submission also swept ``~/.agent-browser/`` for zombies from
   direct CLI invocations outside Hermes. teknium1 marked this
   Blocking on review: those sessions have no ``owner_pid`` file the
   guard can prove ownership through, so identity+session-name
   binding only proves the PID is an agent-browser daemon for that
   session *name*, not that it is orphaned or Hermes-owned. A live
   ``agent-browser open`` in a user's terminal would be terminated by
   the next Hermes restart. The new regression test
   ``TestAppDirSessionsAreLeftAlone::test_live_app_dir_daemon_survives_reaper``
   pins the removal.

The retry calls ``_reap_orphaned_browser_sessions`` unchanged — it
only globs ``agent-browser-h_*``/``-cdp_*``/``-hermes_*`` socket dirs
in the system tmp dir. Direct CLI (app-dir) sessions live in
``~/.agent-browser/`` which is intentionally outside that glob, so
the retry can never reach them either.

## Revalidation on current main

teknium1's second ask was to revalidate the Windows repro on the
upgraded dependency (``agent-browser@^0.26.0``, commit 284e084
which wired daemon idle-timeout). Honest findings:

- On this Windows machine, ``agent-browser`` resolves via bare PATH
  to the user-managed NVM install at 0.17.1 — the Hermes-managed
  bundled prefix ``$HERMES_HOME/node/bin/agent-browser`` was never
  populated (``ensure_browser`` in ``scripts/install.sh`` did not run
  successfully for this user). The idle-timeout commit does NOT apply
  to a 0.17.1 daemon. The zombie-on-restart problem still reproduces
  here, exactly as the PR describes.
- A correctly-installed Hermes that ran ``ensure_browser`` should
  have 0.26.0 (via ``npm install -g --prefix "$HERMES_HOME/node"``)
  prepended to PATH, and the daemon idle-timeout should reduce zombie
  incidence in the steady state. The retry path therefore becomes
  defense-in-depth for two remaining edge cases: (a) the failed-install
  edge case (this machine), where 0.17.1 is still spawned; and
  (b) races where a busy daemon is SIGKILLed or hits a Hyper-V port
  conflict (NousResearch#1041 per the upstream commit) before idle-timeout fires.

I did NOT directly verify the repro on a healthily-installed 0.26.0;
if the maintainers want that, it requires a fully-clean ``hermes
setup --ensure browser`` run on this machine and a force-restart
repro, which I can attempt but haven't done. Flagging the gap rather
than claiming false-green.

## Tests

``tests/tools/test_browser_orphan_reaper.py`` — 32 tests:

- ``TestAppDirSessionsAreLeftAlone`` (1 new) — keystone regression
  teknium1 asked for: a live direct-CLI (.pid in ``~/.agent-browser/``)
  daemon survives ``_reap_orphaned_browser_sessions`` even when the
  identity guard is mocked to permit it.
- ``TestDaemonStartFailureDetection`` (3) — detector behavior.
- ``TestDaemonRetryOnStartFailure`` (2) — retry calls reaper + resets
  session; second failure does not retry again.
- All 26 pre-existing tests in the file unchanged.

Verified on Windows 11 / Python 3.11.15 / pytest 9.0.2:

    pytest tests/tools/test_browser_orphan_reaper.py  # 32 passed
    pytest tests/tools/test_browser_cdp_override.py \
           tests/tools/test_browser_cdp_tool.py \
           tests/tools/test_browser_cleanup.py \
           tests/tools/test_browser_chromium_check.py \
           tests/tools/test_browser_console.py                  # 101 passed

## Files

- ``tools/browser_tool.py`` — retry logic, session reset helper,
  daemon-start-failure detector. The app-dir reaper code from the
  first iteration is removed.
- ``tests/tools/test_browser_orphan_reaper.py`` — new regression
  test; the app-dir test classes TestAppDirReaping and
  TestAppDirBindingGuard are removed (their target code no longer
  exists).

## Out of scope

The original submission mentioned ``AGENT_BROWSER_IDLE_TIMEOUT_MS``
not being implemented in agent-browser v0.17.1 — that was accurate
for 0.17.1 but stale for current main (commit 284e084 wired it
for 0.26.0). That paragraph is removed from the PR body; the retry
is now justified by the SIGKILL/Hyper-V/failed-install edge cases
above, not by absence of idle-timeout.
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.

2 participants