Skip to content

fix(delegate): keep finite stale-timeout for loopback proxy; interrupt idle-stale children - #1

Open
neaucode-bot wants to merge 1 commit into
fix/delegate-task-model-forwardingfrom
fix/delegation-stall-hardening
Open

fix(delegate): keep finite stale-timeout for loopback proxy; interrupt idle-stale children#1
neaucode-bot wants to merge 1 commit into
fix/delegate-task-model-forwardingfrom
fix/delegation-stall-hardening

Conversation

@neaucode-bot

Copy link
Copy Markdown
Owner

Summary

Hardens delegation against the silent multi-minute "stuck subagent" hang (root cause behind a ~10 min client-visible freeze where a healthy 589s / 4.5M-token opus delegate looked dead).

  • Stale-timeout misclassification fix: the stream stale-timeout (and matching httpx read-timeout) was disabled for any loopback base_url, which treats the cursor-openai-api proxy (loopback transport fronting a remote backend) as a slow local model server and removes the only fast stream-recovery path. Now we detect a real local engine via a cached detect_local_server_type probe (gated behind the cheap is_local_endpoint pre-check so remote URLs are never probed); the proxy keeps a finite, context-scaled stale timeout.
  • Large-context ceiling raised to 900s so a legitimate multi-minute prefill survives instead of being killed + reconnected into an infinite re-prefill loop. Explicit HERMES_STREAM_STALE_TIMEOUT / HERMES_STREAM_READ_TIMEOUT overrides still win.
  • Idle-stale children are now interrupted (not mid-tool) instead of only stopping the heartbeat, so a live session recovers without waiting on the gateway drain.

Why stacked on fix/delegate-task-model-forwarding

This builds on the delegation work in NousResearch#48867 and is scoped as a separate, reviewable change. Base it on main once NousResearch#48867 merges, or merge in order.

Test plan

  • tests/agent/test_proxy_stale_timeout.py (cached probe + real production gate: loopback proxy stays finite, detected engine disables, remote never probed, api_key forwarded)
  • tests/tools/test_heartbeat_stale_interrupt.py (idle-stale child interrupted; in-tool child not interrupted)
  • Regression neighbors green: test_delegate, test_model_metadata, test_stream_read_timeout_floor, test_non_stream_stale_timeout (300 passed)

Note: a finite delegation.child_timeout_seconds backstop is an operator config change (not in this PR).

Made with Cursor

…t idle-stale children

The stream stale-timeout (and matching httpx read-timeout) was disabled for
any loopback base_url. That misclassifies the cursor-openai-api proxy — a
loopback transport fronting a remote backend — as a slow local model server
and removes the only fast stream-recovery path, so an upstream stall becomes
an indefinite silent hang.

Detect a *real* local engine via a cached detect_local_server_type probe
(gated behind the cheap is_local_endpoint pre-check so remote URLs are never
probed) instead of trusting the raw loopback address. The proxy now keeps a
finite, context-scaled stale timeout; the large-context ceiling is raised to
900s so a legitimate multi-minute prefill survives. Explicit
HERMES_STREAM_STALE_TIMEOUT / HERMES_STREAM_READ_TIMEOUT overrides still win.

Also interrupt an idle-stale delegate child (not mid-tool) instead of only
stopping the heartbeat, so a live session recovers without waiting on the
gateway drain.

Adds tests for the cached probe, the real production gate, and the
idle-vs-in-tool interrupt behavior.

Co-authored-by: Cursor <cursoragent@cursor.com>
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.

1 participant