fix(runtime): resolve transfer_task caller from pinned session agent - #3893
fix(runtime): resolve transfer_task caller from pinned session agent#3893yangtuooc wants to merge 1 commit into
Conversation
transfer_task inside a background sub-session failed because handleTaskTransfer resolved the calling agent via CurrentAgent(), which returns the shared current-agent (the root coordinator) rather than the agent pinned to the sub-session. Background sub-sessions pin their agent (PinAgent) without mutating currentAgent, so a pipeline dispatched via run_background_agent could not delegate to its own sub-agents. Resolve the caller via resolveSessionAgent(sess), which returns the pinned agent when the session pins one and falls back to CurrentAgent otherwise. Adds a regression test reproducing the background delegation failure. Closes docker#3886
|
/review |
|
👋 Heads up: The |
docker-agent
left a comment
There was a problem hiding this comment.
Assessment: 🟢 APPROVE
The fix is correct: replacing r.CurrentAgent() with r.resolveSessionAgent(sess) properly resolves the calling agent from the session's pinned agent in background sub-sessions, fixing the transfer_task regression described in #3886. The test correctly demonstrates the regression scenario. One low-severity note on a new edge case fallback is posted inline.
| } | ||
|
|
||
| a := r.CurrentAgent() | ||
| a := r.resolveSessionAgent(sess) |
There was a problem hiding this comment.
[low] Silent fallback to current agent when session AgentName is unrecognized
ResolveSession falls back to r.Current() when sess.AgentName is non-empty but the named agent is not found in the team (e.g., if an agent was removed or renamed after the session was created). In that edge case, handleTaskTransfer will validate the transfer against the runtime's current agent instead of the session's intended pinned agent — which could either permit a transfer it should reject, or reject one it should permit.
This is a new code path introduced by this PR: before the change, CurrentAgent() was always used (sessions were never pinned), so the sess.AgentName != "" fallback didn't exist. In practice the risk is low because a session referencing a non-existent agent is already an erroneous state, and Current() is a reasonable fallback. Consider logging a warning when the named agent is not found (inside ResolveSession or here) so it's observable when this fallback fires.
| Confidence | Score |
|---|---|
| 🟡 moderate | 75/100 |
docker-agent
left a comment
There was a problem hiding this comment.
Assessment: 🟢 APPROVE
Sayt-0
left a comment
There was a problem hiding this comment.
The validation fix is correct, but one in-scope issue remains before this can be approved.
handleTaskTransfer now resolves the caller from the pinned session. However, runForwarding immediately resolves callerAgent again from r.currentAgentName(), and SwitchCurrentAgent: true mutates the runtime's shared current-agent state.
For root -> background pipeline -> director, this means:
- validation correctly uses
pipeline; AgentSwitching, hooks, andSubSessionCompletedare still attributed toroot;- the background goroutine temporarily changes the shared current agent to
director, which can affect the foreground session or another concurrent background task.
A focused test against this commit confirmed that AgentSwitching.FromAgent is root instead of pipeline.
Could the session-resolved caller be carried into runForwarding, and could transfers from pinned sessions avoid mutating the shared currentAgent state, with tests covering:
- switching and completion attribution to
pipeline; - the shared current agent remaining
root; - isolation during concurrent background delegation?
The cycle detection and delegation depth guard discussed in #3886 can be tracked as a separate follow-up and does not need to expand this PR.
|
👋 Some commits in this PR are not signed and verified by GitHub. Please sign your commits with a GPG or SSH key registered in your GitHub account, then force-push. Commits that are not verified: See GitHub's guide on signing commits for setup instructions. I've added |
What
Fixes
transfer_taskfailing inside arun_background_agentsub-session. See issue #3886.Problem
A coordinator (
root) fans out work viarun_background_agentto a composite agent (pipeline) that has its ownsub_agents(e.g.director). Whenpipelinecallstransfer_task(agent="director"), it fails with:The error names root as the caller even though the call originates from
pipeline, whose sub-agents do includedirector.Root cause
handleTaskTransferresolves the calling agent viar.CurrentAgent(), which returns the runtime's shared current-agent field. Background sub-sessions are created withPinAgent: true+WithAgentName(cfg.AgentName)(RunAgent→runCollecting), which pins the session topipelinebut deliberately does not mutatecurrentAgent(it staysroot). So validation runs againstroot.SubAgents() = [pipeline]and rejectsdirector.agentRouter.ResolveSessionalready handles pinned sessions ("when sess pins a specific agent (e.g. background agent tasks), that agent is returned directly"), buthandleTaskTransferwasn't using it.Fix
Resolve the caller via
resolveSessionAgent(sess)instead ofCurrentAgent(). This returns the pinned agent when the session pins one, and falls back toCurrentAgent()otherwise — so ordinarytransfer_taskbehavior is unchanged.Tests
Adds
TestHandleTaskTransfer_UsesPinnedSessionAgent: a session pinned topipeline(asrun_background_agentdoes) transfers todirectorwhile the runtime's current-agent isroot.go test ./pkg/runtime/passes.Closes #3886