Skip to content

fix(sequentialagent): recurse task_completed injection and rebind Agent in Run and RunLive - #1510

Merged
wojtas000 merged 5 commits into
google:mainfrom
somuai:fix-nested-sequential-agent-runlive
Sep 13, 2026
Merged

wojtas000 merged 5 commits into
google:mainfrom
somuai:fix-nested-sequential-agent-runlive

Conversation

@somuai

@somuai somuai commented Sep 5, 2026

Copy link
Copy Markdown
Contributor

Fixes #1491

Problem

For a nested sequential agent pipeline like seq(llmA, seq(llmB)):

  1. SequentialAgent.RunLive previously only inspected immediate sub-agents of ctx.Agent() when injecting task_completed and the exit instruction suffix. Because the inner sequential agent is not an llminternal.Agent, its nested LLM sub-agents were never injected, leaving llmB with no mechanism to signal completion during a live run.
  2. When dispatching to sub-agents in RunLive and Run, the existing ctx was passed without rebinding Agent. Consequently, when the inner sequential agent executed, calling ctx.Agent().SubAgents() read the outer pipeline's sub-agents rather than its own, causing duplicate execution of outer agents or recursive loops.

Solution

  1. Recursive injection: injectSubAgents recurses into subAgent.SubAgents() so all nested LLM agents receive the task_completed tool and instruction suffix prior to live session execution.
  2. Context rebinding: Added subInvocationContext wrapping InvocationContext to override Agent() with the specific subAgent being invoked during both Run and RunLive.
  3. Regression tests:
    • Added TestSequentialAgent_RunLive_NestedSequentialInjection verifying that nested LLM agents receive task_completed tool injection.
    • Added TestSequentialAgent_RunLive_NestedSequentialOrchestration verifying event propagation and live request routing through nested sequential pipelines.

Verification

  • go test -v -race ./agent/workflowagents/sequentialagent/... passed cleanly with zero data races.
  • gofmt and go vet passed with 0 warnings.

…nt in Run and RunLive

Fixes google#1491

For a nested sequential agent like seq(llmA, seq(llmB)), RunLive previously
only inspected the immediate sub-agents of ctx.Agent() without recursing into
nested sub-agents, leaving the inner LLM agent without the task_completed
tool or instruction suffix needed to signal completion. Additionally,
invoking the sub-agent's Run and RunLive passed ctx directly without
rebinding Agent, causing nested sequential agents to read the outer agent's
sub-agent list instead of their own.

- Recurse into sub-agents during task_completed injection when subAgent has
  sub-agents.
- Introduce subInvocationContext to rebind Agent to subAgent during sub-agent
  Run and RunLive invocations.
- Add regression unit tests TestSequentialAgent_RunLive_NestedSequentialInjection
  and TestSequentialAgent_RunLive_NestedSequentialOrchestration verifying
  injection and event routing for nested pipelines.

Signed-off-by: SOUMYAJIT GHOSH <[email protected]>
@wojtas000
wojtas000 self-requested a review September 10, 2026 09:37
@wojtas000

wojtas000 commented Sep 10, 2026 •

Copy link
Copy Markdown
Contributor

Summary for the review I just submitted — the body did not come through with it.

Changes requested. The nested-RunLive diagnosis is right and the fix does resolve #1491 — I reproduced both symptoms on the base commit and confirmed this head clears them. Two of the three hunks over-reach, though, and each is a behavior change against main on the 2.x line.

Breaking changes: 2 (both behavioral, both in the inline comments) — injection now permanently rewrites the tools and system instruction of LLM agents nested under a non-live composite, and Run now zeroes Path()/RunID()/SubScheduler() for every sub-agent.

I A/B'd both against 49224b6f762988234bc8933ed344eb4d3979b44e:

base this PR
seq(loop(llmB)) RunLive (fails: loop has no RunLive) — is llmB mutated? no yes, tool added + instruction 12→235 bytes, persists
sub-agent Path()/RunID() under seq.Run() root/seq@1 / run-7 empty / empty

Applying just the two inline suggestions clears both, keeps every test in this PR green, and still passes the nested repro from #1491.

…nner gating, and test hardening

- Revert Run to pass ctx directly to sub-agents, preserving commonContext
  fields (Path, RunID, SubScheduler) without zeroing.
- Gate task_completed injection recursion in RunLive on liveRunner interface
  with a visited set to avoid mutating sub-agents under non-live composites.
- Rebind agent in RunLive using ctx.WithICDelta rather than subInvocationContext,
  eliminating subInvocationContext entirely.
- Add WithICDelta to mockInvocationContext in tests.
- Change non-fatal assertion to t.Fatalf on author check and guard channel
  receives with select on ctx.Done().
- Add TestSequentialAgent_RunLive_NonLiveCompositeNotMutated verifying non-live
  composite children are not mutated.

Signed-off-by: Soumyajit Ghosh <[email protected]>
@somuai

somuai commented Sep 10, 2026

Copy link
Copy Markdown
Contributor Author

Thank you @wojtas000 for the thorough and high-signal review!

All review feedback items have been addressed in commit 93780b8:

  1. Gated injection on live-capable runners with cycle protection: Defined liveRunner interface and gated recursion with else if _, live := subAgent.(liveRunner); live && len(subAgent.SubAgents()) > 0. Added a visited set to defend against cyclic sub-agent graphs. Non-live composite agents like loops will no longer have their nested agents mutated.
  2. Reverted Run to pass ctx directly: Restored subAgent.Run(ctx) in Run, preserving the fast-path in agent.Promote so that Path(), RunID(), and SubScheduler() retain their context values without zeroing.
  3. Rebound RunLive via WithICDelta: Used ctx.WithICDelta(&agent.InvocationContextDelta{Agent: &subAgent}) directly on the invocation context, eliminating subInvocationContext entirely.
  4. Updated mockInvocationContext: Implemented WithICDelta on mockInvocationContext in tests to support clean delta propagation.
  5. Hardened tests:
    • Swapped non-fatal assertions on sub-agent authors to t.Fatalf.
    • Added else { t.Fatalf(...) } guards on llminternal.Agent type assertions so tests cannot pass silently.
    • Wrapped channel receives in select with ctx.Done() timeouts to prevent test hangs.
    • Added TestSequentialAgent_RunLive_NonLiveCompositeNotMutated to verify that nested sub-agents under non-live composites remain untouched.

All package unit tests and the full repository test suite (go test ./...) pass cleanly.

@wojtas000 wojtas000 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM. Thanks for the quick and complete turnaround on this.

I re-checked the two breaking changes on this head: seq(loop(llmB)) now leaves llmB untouched, and a sub-agent under seq.Run() keeps its Path() and RunID(). NonLiveCompositeNotMutated fails against the previous head and passes here, so it pins the gate rather than just describing it. The new tests also fail on base in 0.086s now, instead of running into the package timeout.

One aside, nothing to change: the select guards you added use t.Context(), which only cancels at test cleanup, so they cannot fire while a receive is actually stuck. The t.Fatalf above each one is what prevents the hang — which is why the base run fails fast.

@wojtas000
wojtas000 merged commit 6476af6 into google:main Sep 13, 2026
14 checks passed
kdroste-google pushed a commit to kdroste-google/adk-go that referenced this pull request Sep 29, 2026
…nt in Run and RunLive (google#1510)

* fix(sequentialagent): recurse task_completed injection and rebind Agent in Run and RunLive

Fixes google#1491

For a nested sequential agent like seq(llmA, seq(llmB)), RunLive previously
only inspected the immediate sub-agents of ctx.Agent() without recursing into
nested sub-agents, leaving the inner LLM agent without the task_completed
tool or instruction suffix needed to signal completion. Additionally,
invoking the sub-agent's Run and RunLive passed ctx directly without
rebinding Agent, causing nested sequential agents to read the outer agent's
sub-agent list instead of their own.

- Recurse into sub-agents during task_completed injection when subAgent has
  sub-agents.
- Introduce subInvocationContext to rebind Agent to subAgent during sub-agent
  Run and RunLive invocations.
- Add regression unit tests TestSequentialAgent_RunLive_NestedSequentialInjection
  and TestSequentialAgent_RunLive_NestedSequentialOrchestration verifying
  injection and event routing for nested pipelines.

Signed-off-by: SOUMYAJIT GHOSH <[email protected]>

* fix(sequentialagent): address review feedback on Run context, live runner gating, and test hardening

- Revert Run to pass ctx directly to sub-agents, preserving commonContext
  fields (Path, RunID, SubScheduler) without zeroing.
- Gate task_completed injection recursion in RunLive on liveRunner interface
  with a visited set to avoid mutating sub-agents under non-live composites.
- Rebind agent in RunLive using ctx.WithICDelta rather than subInvocationContext,
  eliminating subInvocationContext entirely.
- Add WithICDelta to mockInvocationContext in tests.
- Change non-fatal assertion to t.Fatalf on author check and guard channel
  receives with select on ctx.Done().
- Add TestSequentialAgent_RunLive_NonLiveCompositeNotMutated verifying non-live
  composite children are not mutated.

Signed-off-by: Soumyajit Ghosh <[email protected]>

---------

Signed-off-by: SOUMYAJIT GHOSH <[email protected]>
Signed-off-by: Soumyajit Ghosh <[email protected]>
Co-authored-by: SOUMYAJIT GHOSH <[email protected]>
Co-authored-by: Jan Wojtas <[email protected]>
li-xuankun312 added a commit to li-xuankun312/einsteinOS that referenced this pull request Sep 29, 2026
…nt in Run and RunLive (google#1510)

* fix(sequentialagent): recurse task_completed injection and rebind Agent in Run and RunLive

Fixes google#1491

For a nested sequential agent like seq(llmA, seq(llmB)), RunLive previously
only inspected the immediate sub-agents of ctx.Agent() without recursing into
nested sub-agents, leaving the inner LLM agent without the task_completed
tool or instruction suffix needed to signal completion. Additionally,
invoking the sub-agent's Run and RunLive passed ctx directly without
rebinding Agent, causing nested sequential agents to read the outer agent's
sub-agent list instead of their own.

- Recurse into sub-agents during task_completed injection when subAgent has
  sub-agents.
- Introduce subInvocationContext to rebind Agent to subAgent during sub-agent
  Run and RunLive invocations.
- Add regression unit tests TestSequentialAgent_RunLive_NestedSequentialInjection
  and TestSequentialAgent_RunLive_NestedSequentialOrchestration verifying
  injection and event routing for nested pipelines.

Signed-off-by: SOUMYAJIT GHOSH <[email protected]>

* fix(sequentialagent): address review feedback on Run context, live runner gating, and test hardening

- Revert Run to pass ctx directly to sub-agents, preserving commonContext
  fields (Path, RunID, SubScheduler) without zeroing.
- Gate task_completed injection recursion in RunLive on liveRunner interface
  with a visited set to avoid mutating sub-agents under non-live composites.
- Rebind agent in RunLive using ctx.WithICDelta rather than subInvocationContext,
  eliminating subInvocationContext entirely.
- Add WithICDelta to mockInvocationContext in tests.
- Change non-fatal assertion to t.Fatalf on author check and guard channel
  receives with select on ctx.Done().
- Add TestSequentialAgent_RunLive_NonLiveCompositeNotMutated verifying non-live
  composite children are not mutated.

Signed-off-by: Soumyajit Ghosh <[email protected]>

---------

Signed-off-by: SOUMYAJIT GHOSH <[email protected]>
Signed-off-by: Soumyajit Ghosh <[email protected]>
Co-authored-by: SOUMYAJIT GHOSH <[email protected]>
Co-authored-by: Jan Wojtas <[email protected]>
wolo-lab added a commit that referenced this pull request Oct 2, 2026
…nt in Run and RunLive (#1510) (#1691)

(cherry picked from commit 6476af6)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Nested SequentialAgent: RunLive never injects the inner pipeline's sub-agent

2 participants