Skip to main content
Tool policy limits which tools the target agent can use during a handoff — so a low-trust gatekeeper cannot escalate privileges by handing off to a powerful automation agent.
Handoffs are secure by default since PR #1848. The target agent only receives tools shared with the source agent. Use tool_policy_mode="passthrough" only when you intentionally need legacy behaviour.

Quick Start

1

Secure by default

No extra configuration needed — handoff(target) already enforces intersect mode.
from praisonaiagents import Agent, handoff, tool

@tool
def search_tool(query: str) -> str:
    """Search the knowledge base."""
    return f"Results for: {query}"

@tool
def execute_code(code: str) -> str:
    """Run code in a sandbox."""
    return "executed"

gatekeeper = Agent(name="Gatekeeper", tools=[search_tool])
automation = Agent(name="Automation", tools=[search_tool, execute_code])

triage = Agent(
    name="Triage",
    handoffs=[handoff(automation)],  # automation only gets search_tool during handoff
)
2

Block dangerous tools

Always strip specific tool names regardless of mode.
from praisonaiagents import Agent, handoff

triage = Agent(
    name="Triage",
    handoffs=[handoff(automation, blocked_tools=["execute_code"])],
)
3

Opt into legacy passthrough

Passthrough gives the target its full tool set (minus blocked_tools). Only use this when the source agent is intentionally delegating more capability than it holds.
from praisonaiagents import Agent, handoff

triage = Agent(
    name="Triage",
    handoffs=[
        handoff(
            automation,
            tool_policy_mode="passthrough",
            blocked_tools=["execute_code"],
        ),
    ],
)
4

Full HandoffConfig

from praisonaiagents import Agent, handoff, HandoffConfig, HandoffToolPolicy

config = HandoffConfig(
    tool_policy=HandoffToolPolicy(
        mode="intersect",
        blocked_tools=["execute_code", "shell_access"],
    ),
)

triage = Agent(
    name="Triage",
    handoffs=[handoff(automation, config=config)],
)

How It Works

Modes

ModeDefault?Target tool setUse when
intersect✅ YesTools both agents have, minus blocked_toolsMulti-agent systems with mixed trust levels (recommended)
passthroughNo (opt-in)Target’s own tools minus blocked_toolsLegacy code, or intentional capability delegation

Configuration Options

HandoffToolPolicy

OptionTypeDefaultDescription
modeLiteral["intersect", "passthrough"]"intersect"How tools are filtered during handoff
blocked_toolsList[str][]Tool names always stripped regardless of mode

handoff() shorthand kwargs

ParameterTypeDefaultDescription
tool_policy_modeOptional[Literal["intersect","passthrough"]]NoneShorthand for config.tool_policy.mode
blocked_toolsOptional[List[str]]NoneShorthand for config.tool_policy.blocked_tools

Common Patterns

Gatekeeper → automation (secure default)

from praisonaiagents import Agent, handoff, tool

@tool
def search_tool(query: str) -> str:
    return f"Results for: {query}"

@tool
def execute_code(code: str) -> str:
    return "executed"

gatekeeper = Agent(name="Gatekeeper", tools=[search_tool])
automation = Agent(name="Automation", tools=[search_tool, execute_code])

router = Agent(name="Router", handoffs=[handoff(automation)])

Always block destructive tools

router = Agent(
    name="Router",
    handoffs=[
        handoff(
            automation,
            blocked_tools=["execute_code", "shell_access", "delete_file"],
        ),
    ],
)

Passthrough with selective block list

router = Agent(
    name="Router",
    handoffs=[
        handoff(
            automation,
            tool_policy_mode="passthrough",
            blocked_tools=["execute_code"],
        ),
    ],
)

Best Practices

Leave tool_policy_mode unset unless you have a specific reason to use passthrough. Intersect mode prevents silent privilege escalation.
Even in intersect mode, add blocked_tools for tools like execute_code or shell_access if they appear in the shared set.
If intersect mode leaves the target without a needed tool, add that tool to the source agent rather than switching to passthrough.
Verify that a gatekeeper handoff cannot invoke tools the source agent does not hold.

Migration Note

Behaviour change (non-breaking API). Before PR #1848, handoffs passed the target agent’s full tool set. After PR #1848, the default is intersection. Existing code runs without changes, but the effective tool set during handoff is narrower. Restore legacy behaviour with one line: tool_policy_mode="passthrough".

Agent Handoffs

Core handoff patterns and delegation

Handoff Filters

Filter context passed during handoff

Handoff Configuration

Full HandoffConfig reference