fix(background_process): do not consume the delivery budget on transient wake-up refusals
start_session_turn() answers 409 retryable=True while the Agent runtime is stale, 409 process_wakeup_paused while wake-ups are paused, and 409 with active_stream_id while the session is busy with another turn. The bridge released the durable claim as a plain failure each time, so a burst of refusals within seconds terminally dropped a completion whose origin session was alive and waiting. release_async_delegation_delivery(..., retryable=True) now forwards the refund to an Agent core whose release_event_delivery() accepts the `retryable` keyword (NousResearch/hermes-agent#120028), detected through inspect.signature, and falls back to the historical plain release on an older core. Hard rejections keep the bounded budget.
This commit is contained in:
1 parent
b1774b45a9
commit
9b6851eaf1
3 files changed
+228
-3
No files matched your search
@@ -1020,6 +1020,29 @@ def _record_async_delegation_accepted(
|
||||
)
|
||||
|
||||
|
||||
def _wakeup_refusal_is_transient(resp: dict | None, status: int) -> bool:
|
||||
"""True when the target refused the wake-up for a reason it will recover from.
|
||||
|
||||
Such refusals are not delivery failures of the completion itself, so they
|
||||
must not consume its bounded durable delivery budget. Otherwise a burst of
|
||||
refusals (e.g. ``agent_runtime_stale`` answers while the Agent runtime is
|
||||
being replaced) terminally drops a completion whose origin session is alive:
|
||||
|
||||
* any payload that explicitly says ``retryable: True`` (stale runtime);
|
||||
* the paused-wakeup contract (``process_wakeup_paused``);
|
||||
* a session busy with another turn (``active_stream_id`` on a 409).
|
||||
"""
|
||||
if not isinstance(resp, dict):
|
||||
return False
|
||||
if resp.get("retryable") is True:
|
||||
return True
|
||||
if status != 409:
|
||||
return False
|
||||
if resp.get("error") == "process_wakeup_paused":
|
||||
return True
|
||||
return bool(resp.get("active_stream_id"))
|
||||
|
||||
|
||||
def _start_async_delegation_wakeup_turn(
|
||||
session_id: str,
|
||||
wakeup_prompt: str,
|
||||
@@ -1059,7 +1082,11 @@ def _start_async_delegation_wakeup_turn(
|
||||
)
|
||||
return
|
||||
|
||||
release_async_delegation_delivery(evt, claim)
|
||||
transient = _wakeup_refusal_is_transient(resp, status)
|
||||
if transient:
|
||||
release_async_delegation_delivery(evt, claim, retryable=True)
|
||||
else:
|
||||
release_async_delegation_delivery(evt, claim)
|
||||
_retry_unclaimed_async_delegation_event(
|
||||
process_registry, evt, keep_legacy_retrying=True
|
||||
)
|
||||
@@ -1068,6 +1095,14 @@ def _start_async_delegation_wakeup_turn(
|
||||
"async delegation wakeup paused for session %s; delivery remains retryable",
|
||||
session_id,
|
||||
)
|
||||
elif transient:
|
||||
logger.info(
|
||||
"async delegation wakeup refused transiently for session %s "
|
||||
"(status=%s err=%r); attempt refunded, durable retry scheduled",
|
||||
session_id,
|
||||
status,
|
||||
(resp or {}).get("error"),
|
||||
)
|
||||
else:
|
||||
logger.debug(
|
||||
"async delegation wakeup not accepted for session %s: "
|
||||
|
||||
Reference in new issue
Block a user