* fix(agents): scope loop detection state per run * fix(agents): harden loop scope fallback * docs: move loop lifecycle detail out of inherited guidance --------- Co-authored-by: PeaceMaker-best <221849497+PeaceMaker-best@users.noreply.github.com> Co-authored-by: Willem Jiang <willem.jiang@gmail.com>
2.1 KiB
Loop detection lifecycle
LoopDetectionMiddleware owns call-pattern detection. Its place in the chain
and audit contract are documented in the
middleware guide.
Run-scoped state
Hash histories, frequency windows/counters, and warning-suppression sets
share a (thread_id, run_id) key. This gives a fresh budget to each user
run when a compiled graph is cached and reused, while keeping one budget
across repeated graph entries belonging to the same Gateway run (including
hidden goal continuations). after_agent clears only transient pending
warnings for its own scope, not those histories; the sync/async
before_agent hooks remain topology-preserving no-ops and must not delete a
sibling run's pending warning. Direct LangGraph embedders may omit
context.run_id; that fallback is anchored to the invocation's shared
Runtime.control object and mapped to an opaque generated ID, because
LangGraph replaces Runtime per node and CPython can reuse freed object
addresses. after_agent releases the anchor mapping, while the bounded map
covers abnormal exits. The compatibility-named
max_tracked_threads limit bounds run scopes, and reset(thread_id) clears
every retained run scope for that thread.
Decision ordering
Loop decisions are severity-first across both detection layers: a warning candidate never short-circuits frequency accounting for the remaining calls in an admitted batch. A hard limit can stop scanning immediately because it rejects the entire batch. Only the selected warning is marked and logged; hash warnings still precede frequency warnings when neither layer stops the run. Among simultaneous frequency-warning candidates, the first crossing in model tool-call order remains selected for compatibility; later calls are still counted and can warn in a later batch. A frequency warning whose burst decays within the batch must not leave a stale suppression mark.
backend/tests/test_loop_detection_middleware.py covers mixed-tool batches,
window decay, overrides, and sync/async compiled-graph execution.