mirror of
https://github.com/NousResearch/hermes-agent.git
synced 2026-10-20 04:21:58 +00:00
feat(codex): honor redirect and hard stop in the app-server runtime
The Codex app-server runtime bypasses the main conversation loop and drives its own subprocess turn, so it needs first-class hooks rather than the OpenAI-loop interrupt path. - `AIAgent.interrupt()` now forwards a hard stop to `CodexAppServerSession.request_interrupt()`, and `redirect()` uses Codex's native `turn/steer` protocol instead of cancelling the subprocess. - `run_turn()` no longer clears an interrupt that arrived during `ensure_started()`: a stop landing mid-startup is honored before `turn/start`, and the interrupt event is cleared on every exit path. - `run_codex_app_server_turn()` mirrors the loop finalizer's interrupt handoff (surface `interrupted` / `interrupt_message`, then `clear_interrupt()`) on both the normal and exception early-return paths, so a hard stop can't leave `_interrupt_requested` stale for the next turn.
This commit is contained in:
parent
cbf5b05c70
commit
f6d2ee4afc
4 changed files with 146 additions and 14 deletions
|
|
@ -702,6 +702,16 @@ def run_codex_app_server_turn(
|
|||
except Exception:
|
||||
pass
|
||||
agent._codex_session = None
|
||||
_user_interrupted = bool(
|
||||
getattr(agent, "_interrupt_requested", False)
|
||||
)
|
||||
_interrupt_message = (
|
||||
getattr(agent, "_interrupt_message", None)
|
||||
if _user_interrupted
|
||||
else None
|
||||
)
|
||||
if _user_interrupted:
|
||||
agent.clear_interrupt()
|
||||
return {
|
||||
"final_response": (
|
||||
f"Codex app-server turn failed: {exc}. "
|
||||
|
|
@ -711,9 +721,27 @@ def run_codex_app_server_turn(
|
|||
"api_calls": 0,
|
||||
"completed": False,
|
||||
"partial": True,
|
||||
"interrupted": _user_interrupted,
|
||||
**(
|
||||
{"interrupt_message": _interrupt_message}
|
||||
if _interrupt_message
|
||||
else {}
|
||||
),
|
||||
"error": str(exc),
|
||||
}
|
||||
|
||||
# This runtime bypasses the normal conversation-loop finalizer. Mirror its
|
||||
# interrupt handoff/cleanup so a hard stop cannot poison the next turn and a
|
||||
# message-bearing compatibility interrupt can still be replayed by callers.
|
||||
_user_interrupted = bool(
|
||||
turn.interrupted and getattr(agent, "_interrupt_requested", False)
|
||||
)
|
||||
_interrupt_message = (
|
||||
getattr(agent, "_interrupt_message", None) if _user_interrupted else None
|
||||
)
|
||||
if _user_interrupted:
|
||||
agent.clear_interrupt()
|
||||
|
||||
# If the turn signalled the underlying client is wedged (deadline
|
||||
# blown, post-tool watchdog tripped, OAuth refresh died, subprocess
|
||||
# exited), retire the session so the next turn respawns codex
|
||||
|
|
@ -819,6 +847,12 @@ def run_codex_app_server_turn(
|
|||
"api_calls": api_calls,
|
||||
"completed": not turn.interrupted and turn.error is None,
|
||||
"partial": turn.interrupted or turn.error is not None,
|
||||
"interrupted": _user_interrupted,
|
||||
**(
|
||||
{"interrupt_message": _interrupt_message}
|
||||
if _interrupt_message
|
||||
else {}
|
||||
),
|
||||
"error": turn.error,
|
||||
# The codex app-server runtime IS an early-return path that bypasses
|
||||
# conversation_loop, but we flush the projected assistant/tool messages
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue