fix: scope tool interrupt signal per-thread to prevent cross-session leaks (#7930)

The interrupt mechanism in tools/interrupt.py used a process-global
threading.Event. In the gateway, multiple agents run concurrently in
the same process via run_in_executor. When any agent was interrupted
(user sends a follow-up message), the global flag killed ALL agents'
running tools — terminal commands, browser ops, web requests — across
all sessions.

Changes:
- tools/interrupt.py: Replace single threading.Event with a set of
  interrupted thread IDs. set_interrupt() targets a specific thread;
  is_interrupted() checks the current thread. Includes a backward-
  compatible _ThreadAwareEventProxy for legacy _interrupt_event usage.
- run_agent.py: Store execution thread ID at start of run_conversation().
  interrupt() and clear_interrupt() pass it to set_interrupt() so only
  this agent's thread is affected.
- tools/code_execution_tool.py: Use is_interrupted() instead of
  directly checking _interrupt_event.is_set().
- tools/process_registry.py: Same — use is_interrupted().
- tests: Update interrupt tests for per-thread semantics. Add new
  TestPerThreadInterruptIsolation with two tests verifying cross-thread
  isolation.
This commit is contained in:
Teknium 2026-04-11 14:02:58 -07:00 committed by GitHub
parent 75380de430
commit dfc820345d
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 183 additions and 78 deletions

View file

@ -924,8 +924,8 @@ def execute_code(
# --- Local execution path (UDS) --- below this line is unchanged ---
# Import interrupt event from terminal_tool (cooperative cancellation)
from tools.terminal_tool import _interrupt_event
# Import per-thread interrupt check (cooperative cancellation)
from tools.interrupt import is_interrupted as _is_interrupted
# Resolve config
_cfg = _load_config()
@ -1114,7 +1114,7 @@ def execute_code(
status = "success"
while proc.poll() is None:
if _interrupt_event.is_set():
if _is_interrupted():
_kill_process_group(proc)
status = "interrupted"
break