Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 11 additions & 1 deletion emrg/server/daemon.py
Original file line number Diff line number Diff line change
Expand Up @@ -714,6 +714,16 @@ def _with_batch_call_nudge(
states it at the tail of the next request, where the next round's calls are
actually decided.

It is a **system** message, not a user one. The content is what the model
reads; the role is what every *other* reader reads. A user turn is the
host's voice: it is the role a client renders as the host's bubble, the role
a transcript attributes to the host, and the role a compaction summarises as
something the host said. This reminder is the daemon's instruction to the
model, so a user role tells every one of those readers that the host said
something the host never typed (reported 2026-11-03). A trailing system
message is accepted where it is needed: measured against the configured
provider, `{"role": "system"}` after the user turn answers normally.

A **new list** is returned and `messages` is never touched, and that is the
whole of "the reminder exists only in this request": the loop's own list is
what `_log_llm_exchange` writes and what a rebuild is rebuilt from, so a
Expand All @@ -727,7 +737,7 @@ def _with_batch_call_nudge(
"""
request = [dict(m) for m in messages]
if previous_round_tool_calls == 1:
request.append({"role": "user", "content": BATCH_CALL_NUDGE})
request.append({"role": "system", "content": BATCH_CALL_NUDGE})
return request


Expand Down
11 changes: 7 additions & 4 deletions tests/test_batch_call_nudge.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,11 @@
requirement stated in the prompt sits too far from the point where the next
round's calls are decided.

So the daemon appends `{"role": "user", "content": "batch call"}` to the
**request** that follows a round dispatching exactly one call. The readings here
So the daemon appends `{"role": "system", "content": "batch call"}` to the
**request** that follows a round dispatching exactly one call. A **system** turn
and not a user one: the reminder is the daemon's instruction, while a user turn
is the host's voice — the role a client renders as the host's bubble and a
transcript attributes to the host. The readings here
are the `messages` lists the provider was actually handed — captured where the
stream is consumed — so nothing asserts the shape of the code that built them:
removing the append, inverting its condition, or moving it ahead of the tool
Expand Down Expand Up @@ -36,7 +39,7 @@
from emrg.session import Session

SESSION_ID = "batch-call-nudge"
NUDGE = {"role": "user", "content": BATCH_CALL_NUDGE}
NUDGE = {"role": "system", "content": BATCH_CALL_NUDGE}


class _StubTool:
Expand Down Expand Up @@ -124,7 +127,7 @@ async def fake_summary(session, records, source=None):


def _nudges(messages: list[dict]) -> int:
return sum(1 for m in messages if m.get("role") == "user"
return sum(1 for m in messages if m.get("role") == "system"
and m.get("content") == BATCH_CALL_NUDGE)


Expand Down
Loading