Invocation Hooks
Hooks let you observe and control agent invocations. Pass a hooks dict as the hooks parameter to invoke_agent():
For a concrete example of an on_tool_call hook in action, see adventure-dungeon.
hooks = {
"on_invoke": [lambda ctx: print(ctx["prompt"])],
"on_tool_call": [lambda ctx: print(f"Tool: {ctx['tool_name']}")],
"on_invoke_complete": [lambda ctx: print(ctx["result"])],
}
result = await obj.invoke_agent(
prompt="Analyze this data.",
hooks=hooks,
)
Hook Types
on_invoke
Fires before the agent starts reasoning. You can inspect the prompt or prevent the invocation.
| Key | Type | Description |
|---|---|---|
role |
str |
The agent's role name |
prompt |
str |
The prompt passed to invoke_agent() |
session |
Session |
The session object |
def log_and_guard(ctx):
print(f"[{ctx['role']}] Prompt: {ctx['prompt']!r}")
return None # allow
# return "Error message" # abort and return to caller
result = await obj.invoke_agent(
prompt="Analyze this data.",
hooks={"on_invoke": [log_and_guard]},
)
The hook receives a context dict with role, prompt, and session. Return None to allow the invocation, or a non-None string to abort it — the agent is not started and the string is returned as an Error to the caller.
on_invoke_complete
Fires after the agent finishes, regardless of outcome.
| Key | Type | Description |
|---|---|---|
role |
str |
The agent's role name |
prompt |
str |
The prompt passed to invoke_agent() |
session |
Session |
The session object |
result |
Any |
The agent's return value or an Error object |
def log_result(ctx):
print(f"[{ctx['role']}] Done: {type(ctx['result']).__name__}")
result = await obj.invoke_agent(
prompt="Analyze this data.",
hooks={"on_invoke_complete": [log_result]},
)
The context dict includes role, prompt, session, and result — the agent's return value or an Error object.
on_tool_call
Fires before each tool execution. Use this to monitor or block specific tools.
| Key | Type | Description |
|---|---|---|
role |
str |
The agent's role name |
session |
Session |
The session object |
tool_name |
str |
The name of the tool being called |
arguments |
dict |
The parsed arguments for the tool |
def block_python_exec(ctx):
if ctx["tool_name"] == "python_exec":
return "Code execution is not allowed"
return None # allow
result = await obj.invoke_agent(
prompt="Write a file and count its lines.",
hooks={"on_tool_call": [block_python_exec]},
)
The context dict includes role, session, tool_name, and arguments.
Return None to allow execution, or a non-None string to deny it — the tool and all remaining tools in the same group are skipped, and the agent is given another reasoning turn.
Recursive Propagation
Hooks are automatically forwarded to every sub-agent invocation via invoke().
If an agentic object calls another via invoke(), the same hooks fire for the sub-agent, giving you visibility across the entire invocation tree.
Note: Hooks are not forwarded when an agent calls another agent's invoke_agent() from within a tool (e.g., self._other.invoke_agent("message")).
In that case, the child agent's invocation receives no hooks — only sub-agent calls via invoke() propagate hooks.
Combining Hooks
Register multiple hooks in the same list. They execute in registration order — the first hook runs before the second.
def log1(ctx):
print(f"[{ctx['role']}] first hook")
def log2(ctx):
print(f"[{ctx['role']}] second hook")
result = await obj.invoke_agent(
prompt="Analyze this data.",
hooks={"on_invoke": [log1, log2]},
)
Both hooks fire when on_invoke is invoked, with log1 executing before log2.