fix(backends,conversations,telegram,ui): a background subagent reports back as its own turn

This commit is contained in:
hh committed 2026-09-04 20:23:33 +02:00
1 parent 2afd3bc3c1
commit 8019c76e53
15 files changed
+608 -91

No files matched your search

+25 -2
View File
@@ -20,7 +20,13 @@ from typing import TYPE_CHECKING, Any, Protocol
import psutil
if TYPE_CHECKING:
from collections.abc import AsyncIterable, AsyncIterator, Iterator, Mapping
from collections.abc import (
AsyncIterable,
AsyncIterator,
Callable,
Iterator,
Mapping,
)
__all__ = ["DEFAULT_TTL", "Session", "SessionClient", "SessionPool", "cgroup_limit"]
@@ -42,7 +48,7 @@ _RSS_HEADROOM = 0.8
class SessionClient(Protocol):
async def connect(self) -> None: ...
async def query(self, prompt: str | AsyncIterable[dict[str, Any]]) -> None: ...
def receive_response(self) -> AsyncIterator[Any]: ...
def receive_messages(self) -> AsyncIterator[Any]: ...
async def interrupt(self) -> None: ...
async def disconnect(self) -> None: ...
@@ -67,6 +73,20 @@ class Session:
state: dict[str, Any] = field(default_factory=dict)
"""Scratch for policy rules (``agents/policy.py``); dies with the process."""
reader: asyncio.Task[None] | None = None
"""Reads the client for the session's whole life, not only during a turn."""
inbox: asyncio.Queue[Any] = field(default_factory=asyncio.Queue)
"""Where the reader puts messages while a turn is attached."""
pending: list[Any] = field(default_factory=list)
"""A turn the CLI started on its own (a background task reported back),
buffered until a consumer attaches."""
attached: bool = False
woken: bool = False
note: str | None = None
"""Summary of the last task notification, the pending turn's prompt."""
observer: Callable[[Any], None] | None = None
"""The last turn's observer; sees subagent traffic between turns."""
@property
def busy(self) -> bool:
return self.lock.locked() or self.running_turn is not None
@@ -192,6 +212,8 @@ class SessionPool:
if session is None:
return
_log.info("closing session %s (%s, %s)", session.session_id, session.kind, key)
if session.reader is not None and session.reader is not asyncio.current_task():
session.reader.cancel()
try:
await session.client.disconnect()
except Exception: # noqa: BLE001
@@ -238,6 +260,7 @@ class SessionPool:
"turns": s.turns,
"busy": s.busy,
"running_turn": s.running_turn,
"pending": len(s.pending),
"pending_question": s.pending_question,
"pinned": s.pinned,
"dirty": s.dirty,