`terminal(background=true, notify_on_complete=true)` appended its watcher descriptor to `process_registry.pending_watchers`, which only the post-turn hooks drain. A process that finished while the turn that launched it was still running (an agent sleep-polling for hours) had no watcher task at all: the completion_queue entry sat inert, nothing was injected, and the chat stayed mute until that turn ended (#112033). - `_register_completion_watcher` arms the watcher on the live gateway loop at registration (`GatewayRunner.arm_process_watcher`, via the existing `_gateway_runner_ref` / `_gateway_loop` seam that send_message and cron already use); `pending_watchers` stays the fallback while the gateway is not serving (checkpoint recovery at startup, shutdown). - The agent-notify branch of `_run_process_watcher` keeps its design (the agent's next turn is the user-facing report) but, when the launching turn is still active at process exit, the injection only queues a follow-up — so the concise receipt is sent to the chat right away instead of never. The busy check is taken before injection because the injected turn itself installs the adapter's session guard. Live probe (real process, real GatewayRunner loop, fake telegram adapter, busy session): before — pending_watchers=1 after exit, 0 watcher tasks, 0 injections, 0 receipts; after — pending_watchers=0, watcher task armed at launch, 1 injection, 1 concise receipt. Control (idle session): 1 injection, 0 receipts, unchanged. Slimmer redo of #112038 by @KoNit-K: same two gaps closed, without a second scheduler registry / loop attribute on ProcessRegistry and GatewayRunner. Co-authored-by: KoNit-K <124019182+KoNit-K@users.noreply.github.com>
50 lines
2.0 KiB
Python
50 lines
2.0 KiB
Python
"""Gateway completion watchers are armed at launch, not only after the turn ends.
|
|
|
|
A ``notify_on_complete`` process that finishes while its launching turn is still running
|
|
used to sit in ``pending_watchers`` until the post-turn drain, leaving the chat mute for
|
|
the whole turn (#112033).
|
|
"""
|
|
|
|
from types import SimpleNamespace
|
|
|
|
from tools.process_registry import ProcessRegistry
|
|
from tools.terminal_tool_background import _register_completion_watcher
|
|
|
|
|
|
def _proc_session():
|
|
return SimpleNamespace(
|
|
id="proc_active", watcher_platform="telegram", watcher_chat_id="123",
|
|
watcher_user_id="u", watcher_user_name="Ada", watcher_thread_id="",
|
|
watcher_message_id="", parent_session_id="",
|
|
)
|
|
|
|
|
|
def _install_runner(monkeypatch, runner):
|
|
import gateway.run as gateway_run
|
|
monkeypatch.setattr(gateway_run, "_gateway_runner_ref", lambda: runner)
|
|
|
|
|
|
def test_live_gateway_arms_watcher_at_registration(monkeypatch):
|
|
armed = []
|
|
_install_runner(monkeypatch, SimpleNamespace(arm_process_watcher=lambda w: armed.append(w) or True))
|
|
registry = ProcessRegistry()
|
|
|
|
_register_completion_watcher(registry, _proc_session(), "agent:main:telegram:dm:123")
|
|
|
|
assert [w["session_id"] for w in armed] == ["proc_active"]
|
|
assert armed[0]["notify_on_complete"] is True and armed[0]["chat_id"] == "123"
|
|
assert registry.pending_watchers == []
|
|
|
|
|
|
def test_watcher_stays_pending_without_a_serving_gateway(monkeypatch):
|
|
"""No runner (CLI-side import) and a runner that is not serving both keep the
|
|
startup / post-turn drain path."""
|
|
registry = ProcessRegistry()
|
|
_install_runner(monkeypatch, None)
|
|
_register_completion_watcher(registry, _proc_session(), "agent:main:telegram:dm:123")
|
|
|
|
_install_runner(monkeypatch, SimpleNamespace(arm_process_watcher=lambda w: False))
|
|
_register_completion_watcher(registry, _proc_session(), "agent:main:telegram:dm:123")
|
|
|
|
assert [w["session_id"] for w in registry.pending_watchers] == ["proc_active", "proc_active"]
|