Skip to content

AgentTool runs the wrapped agent without the parent App's events_compaction_config, so agent-as-tool history grows unbounded #7397

Description

@mahir-m01

Describe the bug

AgentTool.run_async builds a fresh Runner(app_name=..., agent=self.agent, session_service=InMemorySessionService(), ...) for the wrapped agent, without an App. The parent App's events_compaction_config therefore never applies inside the child run.

A wrapped agent that makes many tool calls (for example, a read-only code-navigation agent that greps and reads files) keeps growing its own history until the model server rejects the request because prompt + max_output_tokens exceeds the context window. The resulting ContextWindowExceededError is raised inside the tool call and propagates up, which aborts the parent invocation, even though the parent agent's own history is being compacted correctly.

Where

src/google/adk/tools/agent_tool.py: the Runner(...) construction in run_async (lines ~241-270 in v1.36.1; still constructed the same way on main, around line 265).

To reproduce (outline)

  1. Create an App with EventsCompactionConfig(compaction_interval=5, overlap_size=2, token_threshold=14336, ...).
  2. Root LlmAgent with an AgentTool wrapping a sub-agent whose tools return large outputs (e.g. file reads).
  3. Use a model with a 32k context through LiteLlm (we used vLLM serving Gemma 4 31B, max_model_len=32768).
  4. Let the sub-agent make many tool calls in one invocation: the root agent's events get compacted, but the child session is never compacted and eventually fails with ContextWindowExceededError.

Observed

With google-adk 1.36.1, LiteLlm -> vLLM (Gemma 4 31B, 32,768-token context) across about 130 agent sessions: all 8 context-overflow failures came from the AgentTool child, none from the root agent. Lowering the child's max_output_tokens only moves the failure point (from a ~24.6k-token prompt at 8192 output tokens to ~31.7k at 1024).

Expected behavior

One or more of:

  • child runs inherit the parent App's compaction (and context-caching) configuration;
  • AgentTool accepts an option to pass an App / EventsCompactionConfig for the child run;
  • a child context-window error is returned to the parent agent as a tool error instead of aborting the whole invocation.

Related

#3230 (expand explicit context caching to agent tools and sub agents) covers caching; this issue is about compaction.

Environment

google-adk 1.36.1 (checked against main as of 2026-10-03), Python 3.12, LiteLlm with an OpenAI-compatible vLLM 0.19.1 server.

Activity

  1. added a commit that references this issue on Oct 3, 2026
    fd0f863
  2. mahir-m01 commented on Oct 3, 2026

    @mahir-m01
    Author

    Question for the maintainers: may I open a pull request with the fix below? If AgentTool changes are not wanted on main, would a PR against the v1 branch be preferred instead, where AgentTool is still the standard pattern?

    Some additional context after digging further:

    Precedent. The same gap was fixed for context caching in 9488408 ("propagate context cache config to the AgentTool sub-runner": the sub-runner "never received the parent App's context_cache_config, so context caching was silently off for every wrapped agent"). That change built the nested App with App.model_construct(...) and passed Runner(app=...), and it was rolled back the same day in 5835f5a. I could not find the reason for the rollback in the public history, so I wanted to check here before opening a PR.

    AgentTool and single_turn. I see the AgentTool docstring on main now recommends mode='single_turn' sub-agents, which run inline in the parent's session and therefore already get compaction. The problem still affects AgentTool on main, which remains supported, and every 1.x release, where AgentTool is the standard agent-as-a-tool pattern. We hit it on 1.36.1.

    A tested fix is ready on my fork: main...mahir-m01:adk-python:fix/agent-tool-nested-compaction

    It builds the nested App with the caller's token-threshold compaction settings (token_threshold, event_retention_size). The sliding-window trigger is left out, because it runs after the nested invocation finishes and the nested session is discarded when the tool returns. Building the App also removes this path's use of the deprecated Runner(plugins=...) argument. Verification:

    • A new unit test fails on main and passes with the fix.
    • tox on Python 3.10, 3.11, 3.12, 3.13 and 3.14: all environments pass (17404 passed in each; 17413 on 3.11).
    • All pre-commit hooks pass, with no new mypy errors.
    • A real-model run (gemini-3.1-flash-lite, token_threshold=2000, a wrapped agent reading four long chapters): the wrapped agent's prompt grows to 9 contents on main, and with the fix it is compacted at its 4th request.

    If the earlier rollback points to a preferred approach, for example setting the config on the nested runner's app after construction instead of building the App, I'm happy to adapt the change for either branch.

  3. loyce-cheng commented on Oct 4, 2026

    @loyce-cheng

    Thanks for sharing the fix, @mahir-m01. I reviewed the current main implementation and your commit fd0f863. The configuration is lost when AgentTool creates the nested Runner, and your patch already addresses that path.

    Would additional regression coverage or independent validation of your branch be useful? I can help cover no configuration, sliding-window-only configuration, preservation of the parent's configuration, and repeated or nested AgentTool calls, alongside the existing plugin-lifecycle tests. I have only reviewed the code so far; I have not independently reproduced the reported test results.

    Could the maintainers also clarify whether this should target main or v1, and whether the earlier context-cache rollback affects the preferred way to configure the nested Runner? I would like to coordinate a complementary contribution with you before opening a PR.

  4. added a commit that references this issue on Oct 4, 2026
    81c4368
  5. mahir-m01 commented on Oct 4, 2026

    @mahir-m01
    Author

    Thanks @loyce-cheng, good suggestions. I added tests for those cases:

    • no compaction config, and a sliding-window-only config: the nested run keeps its full history;
    • two consecutive AgentTool calls: each nested run compacts its own history;
    • the caller's EventsCompactionConfig stays unchanged.

    The PR is #7401, against main. Happy to retarget it to v1 if the maintainers prefer.

  6. mahir-m01 commented on Oct 4, 2026

    @mahir-m01
    Author

    @loyce-cheng thanks again for offering to help. If you spot other gaps in this area, for example an AgentTool nested inside another AgentTool, or how this interacts with the plugin lifecycle, a follow-up PR would be very welcome. Reviews and testing on #7401 are welcome too.

  7. added theissue type on Oct 5, 2026
  8. surajksharma07 commented on Oct 6, 2026

    @surajksharma07
    Collaborator

    @mahir-m01 Reproduced on 2.11.0 with a mocked model: the same reader agent gets compacted as root but under AgentTool its request just keeps growing (1 -> 3 -> 5 -> 7 -> 9 contents) and no summary ever shows up.

    Until #7401 is reviewed a workaround worth trying: add a small App plugin whose before_run_callback sets invocation_context.events_compaction_config when it's None to a copy of the App's config with compaction_interval/overlap_size set to None. AgentTool passes parent plugins to the child run by default so the child picks it up. On my side that keeps the child at 3 contents with the summary in place. Could you check whether that holds up on your vLLM/Gemma setup? It won't help if include_plugins=False.

    For #7401 the open question is still why the context cache propagation (9488408) was reverted so please run the PR tests against v1 too in case maintainers want it there.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Labels

tools[Component] This issue is related to tools

Type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions