You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Ask the email agent about a sender whose messages have long bodies and it returns no answer at all — not a wrong answer, nothing. The search succeeds, the results come back, and the model then blows the context window and emits a canned apology.
Verbatim, every time:
I had to trim the conversation to fit my memory but I'm still not making progress. Could you re-ask in a fresh chat with just the essentials?
Reproduced independently on two machines and two operating systems, so this is not platform-specific. Both GPU-only and Gmail-only, both Gemma-4-E4B-it-GGUF at ctx 65536 (runtime-resolved, verified — not the configured value):
box
probe
true
runs
result
macOS / Metal
from:Every newer_than:14d, both phrasings
15
2/2
tool succeeded, then the fallback. No answer.
Linux / vulkan
from:Every, 3 REST + 3 TUI
15
6/6
stated 0, enumerated 0. No answer.
The tool call itself is fine in every run — tool_end success=true, ~1.8s, results returned. Determinism is total: 8 of 8 across both boxes, zero variance.
Root cause path (src/gaia/agents/base/agent.py:3758 streaming, :3907 non-streaming): the LLM call raises, is_ctx_overflow is true, the loop calls _shrink_messages_for_overflow(messages) and retries exactly once (_retried_after_trim_stream), overflows again, and emits that string as final_answer. The fixed single retry is what makes the failure perfectly deterministic — a payload-size cliff alone would show variance near the boundary.
The mechanism underneath is NOT yet measured, and that is the first task here.search_messages sizes its payload via _format_messages_within_budget against envelope_budget_tokens(ctx_size=active_profile_ctx_size()) — 65536 minus the fixed prompt and response reserve — and can shrink bodies to a 200-char floor per message (THREAD_MIN_PER_MESSAGE_CHARS, hub/agents/email/python/gaia_agent_email/tools/read_tools.py:93). Fifteen messages at that floor is ~3K chars, which fits trivially. So either the shrinker is not firing on this path, or the envelope budget ignores what the rest of the conversation already occupies. Do not assume which — measure it.
Two contributing factors worth carrying into the fix:
search_messages ships full message bodies — DEFAULT_BODY_LIMIT_CHARS = 4000 per message (read_tools.py:79) — for questions like "how many emails from X" that need zero body bytes. The bodies are the entire payload cost.
The model widens its own request. In the macOS runs it chose max_results of 100 and 50, not the 25 default, because the phrasing implied breadth ("all messages", "how many"). It asks for more than it can fit.
Part of #2511 (surfaced through T5). Distinct from #2756 / #2760, which fix the wrong count case — a precomputed count cannot help a turn that never reaches the answering stage. #2760 states this boundary explicitly rather than claiming T5 fixed.
Outcome
A counting or listing question against a long-bodied sender returns a real answer instead of the context-overflow apology, on the same GPU/Gmail conditions where it currently fails 8 of 8.
Acceptance criteria
Root cause measured and stated before any fix. For the failing probe, record the serialized envelope size in chars and estimated tokens, whether _format_messages_within_budget fired, the per-message body limit it settled on, and the budget it computed. Expected artifact: a comment on this issue naming which of the two hypotheses holds — shrinker did not fire or budget ignores existing conversation occupancy — with the numbers behind it.
The failing probe answers. Given from:Every newer_than:14d (true 15, Gmail-only, GPU) → the agent states 15, on 3 consecutive runs, with no occurrence of the string "I had to trim the conversation". Today: 0 of 8 runs answered.
A metadata-only path exists for counting questions — a counting/listing ask must not pull 4000-char bodies. Expected: for the same probe, the tool payload contains no message body content, and the envelope size drops by at least an order of magnitude versus today's measured value from criterion 1.
The no-answer path is never silent about why. If a turn still cannot fit, the user-facing text names the actual constraint and a next step, not a generic "re-ask with just the essentials". Expected: the message states the result set was too large and suggests narrowing the query (e.g. a shorter date range).
Unit tests cover the payload-size guard at the registered-tool layer: a seeded backend returning N long-bodied messages produces an envelope under the computed budget. Assert the envelope size, not just that the call returned.
Live-inference check on GPU + Gmail, since the defect is model-context behaviour that unit tests cannot reach. Record the runtime-resolved model id and device, the model's chosen max_results, and the verbatim answer for each of the 3 runs.
Scope & expectations
Where:hub/agents/email/python/gaia_agent_email/tools/read_tools.py (search_messages wrapper :2225-2317, search_messages_impl:841, _format_messages_within_budget:176), hub/agents/email/python/gaia_agent_email/context_budget.py, and the overflow path in src/gaia/agents/base/agent.py:3758 / :3907.
Constraints: GPU-only and Gmail-only for evidence; no new dependencies; this repo's fail-loudly rule applies — a silently truncated result set presented as complete is not an acceptable fix.
How to verify
CLI/agent surface, 3 runs of the currently-failing probe:
gaia tui chat --agent email --query 'how many emails from Every in the last two weeks?'
Attach per run: the verbatim answer, the tool_args the model chose (including max_results), the measured envelope size, and the runtime-resolved model id + device. Plus the ground-truth count from POST /v1/email/search for the same query string.
Problem
Ask the email agent about a sender whose messages have long bodies and it returns no answer at all — not a wrong answer, nothing. The search succeeds, the results come back, and the model then blows the context window and emits a canned apology.
Verbatim, every time:
Reproduced independently on two machines and two operating systems, so this is not platform-specific. Both GPU-only and Gmail-only, both
Gemma-4-E4B-it-GGUFat ctx 65536 (runtime-resolved, verified — not the configured value):from:Every newer_than:14d, both phrasingsfrom:Every, 3 REST + 3 TUIThe tool call itself is fine in every run —
tool_end success=true, ~1.8s, results returned. Determinism is total: 8 of 8 across both boxes, zero variance.Root cause path (
src/gaia/agents/base/agent.py:3758streaming,:3907non-streaming): the LLM call raises,is_ctx_overflowis true, the loop calls_shrink_messages_for_overflow(messages)and retries exactly once (_retried_after_trim_stream), overflows again, and emits that string asfinal_answer. The fixed single retry is what makes the failure perfectly deterministic — a payload-size cliff alone would show variance near the boundary.The mechanism underneath is NOT yet measured, and that is the first task here.
search_messagessizes its payload via_format_messages_within_budgetagainstenvelope_budget_tokens(ctx_size=active_profile_ctx_size())— 65536 minus the fixed prompt and response reserve — and can shrink bodies to a 200-char floor per message (THREAD_MIN_PER_MESSAGE_CHARS,hub/agents/email/python/gaia_agent_email/tools/read_tools.py:93). Fifteen messages at that floor is ~3K chars, which fits trivially. So either the shrinker is not firing on this path, or the envelope budget ignores what the rest of the conversation already occupies. Do not assume which — measure it.Two contributing factors worth carrying into the fix:
search_messagesships full message bodies —DEFAULT_BODY_LIMIT_CHARS = 4000per message (read_tools.py:79) — for questions like "how many emails from X" that need zero body bytes. The bodies are the entire payload cost.max_resultsof 100 and 50, not the 25 default, because the phrasing implied breadth ("all messages", "how many"). It asks for more than it can fit.Part of #2511 (surfaced through T5). Distinct from #2756 / #2760, which fix the wrong count case — a precomputed
countcannot help a turn that never reaches the answering stage. #2760 states this boundary explicitly rather than claiming T5 fixed.Outcome
A counting or listing question against a long-bodied sender returns a real answer instead of the context-overflow apology, on the same GPU/Gmail conditions where it currently fails 8 of 8.
Acceptance criteria
_format_messages_within_budgetfired, the per-message body limit it settled on, and the budget it computed. Expected artifact: a comment on this issue naming which of the two hypotheses holds — shrinker did not fire or budget ignores existing conversation occupancy — with the numbers behind it.from:Every newer_than:14d(true 15, Gmail-only, GPU) → the agent states 15, on 3 consecutive runs, with no occurrence of the string"I had to trim the conversation". Today: 0 of 8 runs answered.max_results, and the verbatim answer for each of the 3 runs.Scope & expectations
hub/agents/email/python/gaia_agent_email/tools/read_tools.py(search_messageswrapper:2225-2317,search_messages_impl:841,_format_messages_within_budget:176),hub/agents/email/python/gaia_agent_email/context_budget.py, and the overflow path insrc/gaia/agents/base/agent.py:3758/:3907.check_followups, which ships compact metadata rows and does not exhibit this.How to verify
CLI/agent surface, 3 runs of the currently-failing probe:
Attach per run: the verbatim answer, the
tool_argsthe model chose (includingmax_results), the measured envelope size, and the runtime-resolved model id + device. Plus the ground-truth count fromPOST /v1/email/searchfor the same query string.