diff --git a/pkg/runtime/agent_delegation.go b/pkg/runtime/agent_delegation.go index ad2b75422..eb3b4f58a 100644 --- a/pkg/runtime/agent_delegation.go +++ b/pkg/runtime/agent_delegation.go @@ -77,8 +77,6 @@ type SubSessionConfig struct { Title string // ToolsApproved overrides whether tools are pre-approved in the child session. ToolsApproved bool - // Thinking propagates the parent's thinking-mode flag. - Thinking bool // PinAgent, when true, pins the child session to AgentName via // session.WithAgentName. This is required for concurrent background // tasks that must not share the runtime's mutable currentAgent field. @@ -110,7 +108,7 @@ func newSubSession(parent *session.Session, cfg SubSessionConfig, childAgent *ag session.WithMaxConsecutiveToolCalls(childAgent.MaxConsecutiveToolCalls()), session.WithTitle(cfg.Title), session.WithToolsApproved(cfg.ToolsApproved), - session.WithThinking(cfg.Thinking), + session.WithThinking(childAgent.ThinkingConfigured()), session.WithSendUserMessage(false), session.WithParentID(parent.ID), } @@ -121,8 +119,8 @@ func newSubSession(parent *session.Session, cfg SubSessionConfig, childAgent *ag } // runSubSessionForwarding runs a child session within the parent, forwarding all -// events to the caller's event channel and propagating session state (tool -// approvals, thinking) back to the parent when done. +// events to the caller's event channel and propagating tool approval state +// back to the parent when done. // // This is the "interactive" path used by transfer_task where the parent agent // loop is blocked while the child executes. @@ -137,7 +135,6 @@ func (r *LocalRuntime) runSubSessionForwarding(ctx context.Context, parent, chil } parent.ToolsApproved = child.ToolsApproved - parent.Thinking = child.Thinking parent.AddSubSession(child) evts <- SubSessionCompleted(parent.ID, child, callerAgent) @@ -216,7 +213,6 @@ func (r *LocalRuntime) RunAgent(ctx context.Context, params agenttool.RunParams) AgentName: params.AgentName, Title: "Background agent task", ToolsApproved: true, - Thinking: sess.Thinking, PinAgent: true, } @@ -252,35 +248,28 @@ func (r *LocalRuntime) handleTaskTransfer(ctx context.Context, sess *session.Ses slog.Debug("Transferring task to agent", "from_agent", a.Name(), "to_agent", params.Agent, "task", params.Task) - ca := r.CurrentAgentName() - // Emit agent switching start event - evts <- AgentSwitching(true, ca, params.Agent) + evts <- AgentSwitching(true, a.Name(), params.Agent) r.setCurrentAgent(params.Agent) defer func() { - r.setCurrentAgent(ca) + r.setCurrentAgent(a.Name()) // Emit agent switching end event - evts <- AgentSwitching(false, params.Agent, ca) + evts <- AgentSwitching(false, params.Agent, a.Name()) // Restore original agent info in sidebar - if originalAgent, err := r.team.Agent(ca); err == nil { - evts <- AgentInfo(originalAgent.Name(), getAgentModelID(originalAgent), originalAgent.Description(), originalAgent.WelcomeMessage()) - } + evts <- AgentInfo(a.Name(), getAgentModelID(a), a.Description(), a.WelcomeMessage()) }() // Emit agent info for the new agent - if newAgent, err := r.team.Agent(params.Agent); err == nil { - evts <- AgentInfo(newAgent.Name(), getAgentModelID(newAgent), newAgent.Description(), newAgent.WelcomeMessage()) - } - - slog.Debug("Creating new session with parent session", "parent_session_id", sess.ID, "tools_approved", sess.ToolsApproved, "thinking", sess.Thinking) - child, err := r.team.Agent(params.Agent) if err != nil { return nil, err } + evts <- AgentInfo(child.Name(), getAgentModelID(child), child.Description(), child.WelcomeMessage()) + + slog.Debug("Creating new session with parent session", "parent_session_id", sess.ID, "tools_approved", sess.ToolsApproved) cfg := SubSessionConfig{ Task: params.Task, @@ -288,7 +277,6 @@ func (r *LocalRuntime) handleTaskTransfer(ctx context.Context, sess *session.Ses AgentName: params.Agent, Title: "Transferred task", ToolsApproved: sess.ToolsApproved, - Thinking: sess.Thinking, } s := newSubSession(sess, cfg, child) diff --git a/pkg/runtime/agent_delegation_test.go b/pkg/runtime/agent_delegation_test.go index c7f2f1c75..bde424fc9 100644 --- a/pkg/runtime/agent_delegation_test.go +++ b/pkg/runtime/agent_delegation_test.go @@ -73,7 +73,6 @@ func TestNewSubSession(t *testing.T) { AgentName: "worker", Title: "Test task", ToolsApproved: true, - Thinking: true, } s := newSubSession(parent, cfg, childAgent) @@ -81,7 +80,7 @@ func TestNewSubSession(t *testing.T) { assert.Equal(t, parent.ID, s.ParentID) assert.Equal(t, "Test task", s.Title) assert.True(t, s.ToolsApproved) - assert.True(t, s.Thinking) + assert.False(t, s.Thinking) // childAgent has no ThinkingConfigured assert.False(t, s.SendUserMessage) assert.Equal(t, 10, s.MaxIterations) // AgentName should NOT be set when PinAgent is false @@ -162,6 +161,40 @@ func TestSubSessionConfig_DefaultValues(t *testing.T) { assert.Empty(t, s.AgentName) } +func TestSubSessionConfig_ThinkingFromChildAgent(t *testing.T) { + parent := session.New(session.WithUserMessage("hello")) + + t.Run("child agent without thinking configured gets thinking=false even if parent has thinking=true", func(t *testing.T) { + parent.Thinking = true + + childAgent := agent.New("haiku-worker", "") + + cfg := SubSessionConfig{ + Task: "simple task", + AgentName: "haiku-worker", + Title: "Transferred task", + } + + s := newSubSession(parent, cfg, childAgent) + assert.False(t, s.Thinking, "sub-session should NOT inherit parent's thinking when child agent has no thinking_budget") + }) + + t.Run("child agent with thinking configured gets thinking=true", func(t *testing.T) { + parent.Thinking = false + + childAgent := agent.New("opus-worker", "", agent.WithThinkingConfigured(true)) + + cfg := SubSessionConfig{ + Task: "complex task", + AgentName: "opus-worker", + Title: "Transferred task", + } + + s := newSubSession(parent, cfg, childAgent) + assert.True(t, s.Thinking, "sub-session should have thinking enabled when child agent has thinking_budget") + }) +} + func TestSubSessionConfig_InheritsAgentLimits(t *testing.T) { parent := session.New(session.WithUserMessage("hello")) diff --git a/pkg/runtime/skill_runner.go b/pkg/runtime/skill_runner.go index 3cdc19bb2..2f4ca3d9b 100644 --- a/pkg/runtime/skill_runner.go +++ b/pkg/runtime/skill_runner.go @@ -74,10 +74,13 @@ func (r *LocalRuntime) handleRunSkill(ctx context.Context, sess *session.Session AgentName: ca, Title: "Skill: " + params.Name, ToolsApproved: sess.ToolsApproved, - Thinking: sess.Thinking, } s := newSubSession(sess, cfg, a) + // Skills run as the same agent, so they inherit the session's current + // thinking state (which may have been toggled by the user via /think) + // rather than the agent's static config default. + s.Thinking = sess.Thinking return r.runSubSessionForwarding(ctx, sess, s, span, evts, ca) }