mirror of
https://github.com/rennf93/roboco.git
synced 2026-08-03 07:23:24 +02:00
fix(prompter): keep a watched intake chat alive (idle-reap counted reading as idle) (#379)
* fix(prompter): keep a watched intake chat alive (idle-reap counted reading as idle) Intake chats "dropped after a while" — the panel showed "Live connection lost". The idle reaper retires an interactive session whose last_activity is older than interactive_idle_reap_seconds (30m default), but last_activity was bumped only by an agent event or a human turn. An open SSE stream — the human reading a proposed draft / MegaTask spec without typing — bumped nothing, so a chat under active review was reaped mid-read, closing the stream (the SSE transport error the panel reports as "Live connection lost"). stream() now runs a keepalive task that refreshes last_activity every 60s while the stream is connected, so an open, actively-watched chat counts as alive; when the tab closes the generator ends, the keepalive is cancelled, and a genuinely abandoned chat still reaps after the threshold. The keepalive runs beside an un-cancelled queue.get() so no live token or the close sentinel can be dropped. * fix(tests): conventions PR integration test honors the #375 workspace-scope guard #375 added a containment guard to open_conventions_pr (workspace_path must sit under {workspaces_root}/{slug}); the unit test was updated but this integration test still seeded a bare tmp_path/repo, so open_conventions_pr returned None and test_open_conventions_pr_commits_locally_without_remote failed on master. Anchor workspaces_root at the test dir and place the repo under the project's slug. --------- Co-authored-by: Renn F <rennf93@users.noreply.github.com>
This commit is contained in:
@@ -8,6 +8,7 @@ import time
|
||||
|
||||
import httpx
|
||||
import pytest
|
||||
import roboco.services.prompter_live as pl
|
||||
from roboco.services.prompter_live import (
|
||||
PrompterLiveRegistry,
|
||||
get_live_registry,
|
||||
@@ -151,6 +152,35 @@ async def test_stream_yields_queued_events_then_ends_on_close() -> None:
|
||||
]
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_stream_keepalive_keeps_watched_chat_alive(
|
||||
monkeypatch: pytest.MonkeyPatch,
|
||||
) -> None:
|
||||
"""An open SSE stream refreshes last_activity even with no events, so a
|
||||
human reading a proposal (tab open, not typing) is not idle-reaped. Without
|
||||
this the chat "drops after a while" mid-review."""
|
||||
monkeypatch.setattr(pl, "_STREAM_KEEPALIVE_SECONDS", 0.01)
|
||||
reg = PrompterLiveRegistry()
|
||||
session = reg.open("s1", "intake-1")
|
||||
session.last_activity = time.monotonic() - 4000 # silent for >1h
|
||||
|
||||
# Before anyone connects, the abandoned-looking chat IS idle-reapable.
|
||||
assert ("s1", "intake-1") in reg.idle_session_ids(1800)
|
||||
|
||||
async def watch() -> None:
|
||||
async for _ in reg.stream("s1"):
|
||||
pass
|
||||
|
||||
task = asyncio.create_task(watch())
|
||||
try:
|
||||
await asyncio.sleep(0.05) # let a keepalive tick fire on the open stream
|
||||
# The connected stream refreshed activity → no longer idle.
|
||||
assert reg.idle_session_ids(1800) == []
|
||||
finally:
|
||||
reg.close("s1")
|
||||
await asyncio.wait_for(task, timeout=1.0)
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_stream_unknown_session_is_empty() -> None:
|
||||
reg = PrompterLiveRegistry()
|
||||
|
||||
Reference in New Issue
Block a user