fix: suppress duplicate completion notifications when agent already consumed output via wait/poll/log (#8228)

When the agent calls process(action='wait') or process(action='poll')
and gets the exited status, the completion_queue notification is
redundant — the agent already has the output from the tool return.
Previously, the drain loops in CLI and gateway would still inject
the [SYSTEM: Background process completed] message, causing the
agent to receive the same information twice.

Fix: track session IDs in _completion_consumed set when wait/poll/log
returns an exited process. Drain loops in cli.py and gateway watcher
skip completion events for consumed sessions. Watch pattern events
are never suppressed (they have independent semantics).

Adds 4 tests covering wait/poll/log marking and running-process
negative case.
This commit is contained in:
Teknium 2026-04-12 00:36:22 -07:00 committed by GitHub
parent fdf55e0fe9
commit f53a5a7fe1
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 92 additions and 5 deletions

View file

@ -289,3 +289,62 @@ class TestCodeExecutionBlocked:
def test_notify_on_complete_blocked_in_sandbox(self):
from tools.code_execution_tool import _TERMINAL_BLOCKED_PARAMS
assert "notify_on_complete" in _TERMINAL_BLOCKED_PARAMS
# =========================================================================
# Completion consumed suppression
# =========================================================================
class TestCompletionConsumed:
"""Test that wait/poll/log suppress redundant completion notifications."""
def test_wait_marks_completion_consumed(self, registry):
"""wait() returning exited status marks session as consumed."""
s = _make_session(sid="proc_wait", notify_on_complete=True, output="done")
s.exited = True
s.exit_code = 0
registry._running[s.id] = s
with patch.object(registry, "_write_checkpoint"):
registry._move_to_finished(s)
# Notification is in the queue
assert not registry.completion_queue.empty()
assert not registry.is_completion_consumed("proc_wait")
# Agent calls wait() — gets the result directly
result = registry.wait("proc_wait", timeout=1)
assert result["status"] == "exited"
# Now the completion is marked as consumed
assert registry.is_completion_consumed("proc_wait")
def test_poll_marks_completion_consumed(self, registry):
"""poll() returning exited status marks session as consumed."""
s = _make_session(sid="proc_poll", notify_on_complete=True, output="done")
s.exited = True
s.exit_code = 0
registry._finished[s.id] = s
result = registry.poll("proc_poll")
assert result["status"] == "exited"
assert registry.is_completion_consumed("proc_poll")
def test_log_marks_completion_consumed(self, registry):
"""read_log() on exited session marks as consumed."""
s = _make_session(sid="proc_log", notify_on_complete=True, output="line1\nline2")
s.exited = True
s.exit_code = 0
registry._finished[s.id] = s
result = registry.read_log("proc_log")
assert result["status"] == "exited"
assert registry.is_completion_consumed("proc_log")
def test_running_process_not_consumed(self, registry):
"""poll() on a still-running process does not mark as consumed."""
s = _make_session(sid="proc_running", notify_on_complete=True, output="partial")
registry._running[s.id] = s
result = registry.poll("proc_running")
assert result["status"] == "running"
assert not registry.is_completion_consumed("proc_running")