fix(agent): stop cron and subagent runs auto-titling their sessions
The turn prologue titles every session, and it is shared by every agent — including the ones no person is reading. A cron job already names its own session after the job in its finally block, so the titler spent a side-LLM call per fire to write the delivery scaffolding over it for the length of the run. A delegated child's session is hidden from every picker, so a batch at max_concurrent_children paid N title calls for N names nobody opens. Both are the same class of run that already sets skip_memory to stay off the auxiliary path, so keep the titler off it too.
This commit is contained in:
@@ -170,18 +170,36 @@ def append_notes_to_multimodal_content(content: Any, notes: str) -> bool:
|
||||
return False
|
||||
|
||||
|
||||
# Surfaces whose sessions must not be auto-titled. The prologue is shared by
|
||||
# EVERY agent, not only the ones a human is watching, so membership here is what
|
||||
# keeps the titler off machine-driven runs:
|
||||
#
|
||||
# - cron — the scheduler names its own session after the job in its `finally`
|
||||
# block, and the opener is the cron delivery hint, not a user's request.
|
||||
# Titling it writes that scaffolding as the visible name for the whole run and
|
||||
# bills a side-LLM call per fire, against the same job that sets
|
||||
# `skip_memory` / `skip_background_review` to avoid exactly that.
|
||||
# - subagent — a delegated child's session is hidden from every picker, so its
|
||||
# title is never read. A batch at `max_concurrent_children` would pay N title
|
||||
# calls for N names nobody sees.
|
||||
_UNTITLED_PLATFORMS = frozenset({"cron", "subagent"})
|
||||
|
||||
|
||||
def _maybe_title_session_at_turn_start(agent: Any, messages: List[Any]) -> None:
|
||||
"""Kick off auto-titling for this session's first user message.
|
||||
|
||||
Called from the turn prologue, so every surface (CLI, gateway, TUI/desktop,
|
||||
ACP) gets identical behavior without each one re-implementing the call.
|
||||
Fully defensive: titling is cosmetic and must never break a turn.
|
||||
Called from the turn prologue, so every surface a human reads (CLI, gateway,
|
||||
TUI/desktop, ACP) gets identical behavior without each one re-implementing
|
||||
the call. Fully defensive: titling is cosmetic and must never break a turn.
|
||||
"""
|
||||
session_db = getattr(agent, "_session_db", None)
|
||||
session_id = getattr(agent, "session_id", None)
|
||||
if not session_db or not session_id:
|
||||
return
|
||||
|
||||
if str(getattr(agent, "platform", "") or "").lower() in _UNTITLED_PLATFORMS:
|
||||
return
|
||||
|
||||
try:
|
||||
from agent.message_content import flatten_message_text
|
||||
from agent.title_generator import maybe_auto_title
|
||||
|
||||
@@ -365,13 +365,44 @@ def test_between_turns_refresh_adds_late_tool_when_servers_registered():
|
||||
assert any(t["function"]["name"] == "mcp_x_tool" for t in agent.tools)
|
||||
|
||||
|
||||
class _TitlingAgent:
|
||||
"""Only what ``_maybe_title_session_at_turn_start`` reads off an agent."""
|
||||
|
||||
def __init__(self, platform):
|
||||
self.platform = platform
|
||||
self.session_id = "sess-1"
|
||||
self.model = "test/model"
|
||||
self.provider = "openrouter"
|
||||
self.base_url = "https://openrouter.ai/api/v1"
|
||||
self.api_key = "sk-x"
|
||||
self.api_mode = "chat_completions"
|
||||
self._session_db = MagicMock()
|
||||
self._session_db_created = True
|
||||
|
||||
|
||||
def _title_turn(platform, message="Fix the login button"):
|
||||
"""Run the prologue's titling step and return the maybe_auto_title mock."""
|
||||
from agent import turn_context
|
||||
|
||||
with patch("agent.title_generator.maybe_auto_title") as titler:
|
||||
turn_context._maybe_title_session_at_turn_start(
|
||||
_TitlingAgent(platform),
|
||||
[{"role": "user", "content": message}],
|
||||
)
|
||||
return titler
|
||||
|
||||
|
||||
@pytest.mark.parametrize("platform", ["cli", "telegram", "desktop", "acp", None])
|
||||
def test_prologue_titles_the_surfaces_a_person_reads(platform):
|
||||
assert _title_turn(platform).called
|
||||
|
||||
|
||||
@pytest.mark.parametrize("platform", ["cron", "CRON", "subagent"])
|
||||
def test_prologue_does_not_title_machine_driven_runs(platform):
|
||||
"""Cron names its own session after the job, and nobody opens a subagent's.
|
||||
|
||||
|
||||
|
||||
Both would otherwise pay a side-LLM call per run for a name that is either
|
||||
overwritten or never read.
|
||||
"""
|
||||
assert not _title_turn(platform).called
|
||||
|
||||
|
||||
Reference in New Issue
Block a user