fix(gateway): split turn-hold expiry from idle-timeout failure path

Introduce HygieneTurnHoldExceeded exception so turn-hold budget expiry
no longer collapses into the generic asyncio.TimeoutError handler.

- Add HygieneTurnHoldExceeded exception (availability boundary, not a failure)
- Add dedicated handler: stamps AGENT_COMPRESSION_TURNHOLD provenance,
  sends deferral notice, does NOT increment failure cooldown
- Preserve #87011 contract: idle timeout still sends 'no output' message
  and takes failure path
- Add behavior witnesses: turn-hold ≠ idle timeout, cooldown untouched

Fixes semantic boundary collapse flagged in PR #90845 review.
This commit is contained in:
Machan-Army
2026-08-20 20:13:45 +00:00
committed by kshitij
parent 543c85acbf
commit 31b974dfb7
3 changed files with 262 additions and 1 deletions
+1
View File
@@ -37,6 +37,7 @@ class ActivityProvenance(str, Enum):
AGENT_COMPRESSION = "agent.compression"
AGENT_COMPRESSION_TIMEOUT = "agent.compression_timeout"
AGENT_COMPRESSION_COOLDOWN = "agent.compression_cooldown"
AGENT_COMPRESSION_TURNHOLD = "agent.compression_turnhold"
def bound_activity_description(description: Optional[str]) -> str: