fix(agent): catch cross-agent turn overlaps in the tripwire (#64934) - #67337
fix(agent): catch cross-agent turn overlaps in the tripwire (#64934)#67337Hotragn wants to merge 1 commit into
Conversation
…arch#64934) note_turn_start kept its in-flight marker on the agent object, but the gateway caches agents per routing key (_agent_cache) while transcripts are owned by session_id — and switch_session (/resume from a second surface, CLI-continuity rebinding, async-delegation pinning, topic-binding tip-walks) maps multiple routing keys onto one session_id without any cross-key check. Two keys mapped to one session run concurrent turns on two different agent objects, so the per-agent tripwire could never fire for exactly the dispatch route NousResearch#64934 is waiting to identify. Add a module-level session_id-keyed in-flight registry alongside the per-agent marker. Same philosophy as the original tripwire: log-only, takes ownership on overlap, under-reports rather than double-reports (a same-agent overlap warns once, not twice). The persist-time clear pops the session id stamped at turn start, so a mid-turn compression rotation of agent.session_id cannot strand the slot.
|
Thanks for tracing this cross-key route. Current The existing tripwire is per-agent state ( No correctness or design-fit issue was found in the two-file diff at Automated hermes-sweeper review. |
|
Merged via PR #67371 — your commit was cherry-picked onto current main with your authorship preserved in git log ( One follow-up we added on top: background-review forks share the live parent's Thanks for the contribution! |
What does this PR do?
Makes the #64934 turn-overlap tripwire able to see the overlap route it was merged to identify.
note_turn_start(merged in #65499) keeps its in-flight marker on the agent object. But the gateway caches agents per routing key (_agent_cache[session_key],gateway/run.py:19815), while the durable transcript is owned by session_id — and the key→id mapping is many-to-one on current main:SessionStore.switch_session()(gateway/session.py:2394) re-points a routing key at any target session with no check that another live key already routes there. Shipped callers that create this state:/resumeof a named session from a second surface, CLI-continuity rebinding (gateway/run.py:8075-8079), async-delegation completion pinning (gateway/run.py:11800, #57498), and the Telegram topic-binding compression-tip walk (gateway/run.py:11828-11852).Once two routing keys map to one session_id, every guard in the dispatch path (
_active_sessionsin the adapter,_running_agentsin the runner — both keyed by routing key) admits a second concurrent turn on the same transcript, and the two turns run on two different agent objects — so the per-agent tripwire stays silent no matter how long production runs. The "wait for the tripwire to name the route" plan on #64934 cannot converge on this route without this change.This PR adds a module-level, session_id-keyed in-flight registry alongside the per-agent marker. Same design contract as the original tripwire: log-only (no behavior change to dispatch or persistence), takes ownership of the slot on overlap, and under-reports rather than double-reports — a same-agent overlap still warns exactly once, and the first persist clears the slot unconditionally. The persist-time clear pops the session id stamped at turn start, so a mid-turn compression rotation of
agent.session_idcan't strand the slot or misfire on the rotated id.Full route analysis with a runnable reproduction (real
SessionStoreagainst a tempHERMES_HOME) is in my comment on #64934.Related Issue
Part of #64934 (diagnostic half — the issue stays open for the serialization fix). Extends #65499.
Type of Change
Changes Made
agent/agent_runtime_helpers.py— add_INFLIGHT_TURNS_BY_SESSIONregistry + lock;note_turn_startgains a cross-agent leg that warns when a different in-flight turn holds the same session_id under another agent object (dedup'd against the per-agent warning);note_turn_persistedclears the registry slot via the session id stamped at turn start.tests/agent/test_turn_overlap_tripwire.py— 6 new behavior tests: cross-agent overlap warns with both turn ids; serial cross-agent turns are silent; distinct sessions never cross-warn; same-agent overlap does not double-report; mid-turn session rotation still releases the slot; crashed turn warns once then recovers. Plus an autouse fixture isolating the module registry between tests.How to Test
scripts/run_tests.sh tests/agent/test_turn_overlap_tripwire.py— 10 passed. Also rantests/agent/ tests/run_agent/ tests/hermes_state/(415 files) on native Windows and diffed the failing-test set against a pristinemainworktree: identical (the failures are this machine's pre-existing native-Windows set — LSP tooling, shell hooks, sandbox-mirror tests; CI is Linux). Zero failures attributable to this change.note_turn_starton two agent objects previously produced no warning; with this PR it names both turn ids and the shared session.Checklist
Code
fix(scope):,feat(scope):, etc.)scripts/run_tests.sh tests/agent/ tests/run_agent/ tests/hermes_state/) — failing set on native Windows is byte-identical to pristinemain(pre-existing platform failures only; the changed file's suite is 10/10)Documentation & Housekeeping
cli-config.yaml.exampleif I added/changed config keys — N/ACONTRIBUTING.mdorAGENTS.mdif I changed architecture or workflows — N/Athreading/time, no platform surface; developed and tested on native Windows