Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 12 additions & 11 deletions pkg/runtime/agent_delegation.go
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,8 @@ type SubSessionConfig struct {
Title string
// ToolsApproved overrides whether tools are pre-approved in the child session.
ToolsApproved bool
// Permissions defines session-level tool permission overrides.
Permissions *session.PermissionsConfig
// NonInteractive marks the child session as running without a user present
// (e.g. MCP server, A2A adapter, background agent). This causes the runtime
// to auto-stop on max iterations instead of blocking for user input.
Expand Down Expand Up @@ -184,6 +186,9 @@ func newSubSession(parent *session.Session, cfg SubSessionConfig, childAgent *ag
if cfg.PinAgent {
opts = append(opts, session.WithAgentName(cfg.AgentName))
}
if cfg.Permissions != nil {
opts = append(opts, session.WithPermissions(cfg.Permissions))
}
// Merge parent's excluded tools with config's excluded tools so that
// nested sub-sessions (e.g. skill → transfer_task → child) inherit
// exclusions from all ancestors and don't re-introduce filtered tools.
Expand Down Expand Up @@ -476,23 +481,18 @@ func (r *LocalRuntime) CurrentAgentSubAgentNames() []string {
// RunAgent implements agenttool.Runner. It starts a sub-agent synchronously
// and blocks until completion or cancellation.
//
// Background tasks run with tools pre-approved because there is no user
// present to respond to interactive approval prompts during async
// execution. This is a deliberate design trade-off: the user implicitly
// authorises all tool calls made by the sub-agent when they approve
// run_background_agent. Callers should be aware that prompt injection in
// the sub-agent's context could exploit this gate-bypass.
//
// TODO: propagate the parent session's per-tool permission rules once the
// runtime supports per-session permission scoping rather than a single
// shared ToolsApproved flag.
// Background tasks inherit the parent session's permissions because there is no user
// present to respond to interactive approval prompts during async execution.
// Tool calls that result in an "Ask" outcome will be auto-denied by the dispatcher
// due to the non-interactive context.
func (r *LocalRuntime) RunAgent(ctx context.Context, params agenttool.RunParams) *agenttool.RunResult {
return r.runCollecting(ctx, params.ParentSession, SubSessionConfig{
Task: params.Task,
ExpectedOutput: params.ExpectedOutput,
AgentName: params.AgentName,
Title: "Background agent task",
ToolsApproved: true,
ToolsApproved: params.ParentSession.ToolsApproved,
Permissions: params.ParentSession.Permissions,
NonInteractive: true,
PinAgent: true,
}, params.OnContent)
Expand Down Expand Up @@ -552,6 +552,7 @@ func (r *LocalRuntime) handleTaskTransfer(ctx context.Context, sess *session.Ses
AgentName: params.Agent,
Title: "Transferred task",
ToolsApproved: sess.ToolsApproved,
Permissions: sess.Permissions,
NonInteractive: sess.NonInteractive,
},
SwitchCurrentAgent: true,
Expand Down
1 change: 1 addition & 0 deletions pkg/runtime/skill_runner.go
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,7 @@ func (r *LocalRuntime) RunSkillFork(ctx context.Context, sess *session.Session,
AgentName: ca,
Title: "Skill: " + prepared.SkillName,
ToolsApproved: sess.ToolsApproved,
Permissions: sess.Permissions,
NonInteractive: sess.NonInteractive,
ExcludedTools: []string{skills.ToolNameRunSkill},
AllowedTools: prepared.AllowedTools,
Expand Down
Loading