Hooks & Events
✓ Fresh · 2026-02-24Pi provides a comprehensive event system that extensions can hook into. These events cover the full lifecycle of sessions, messages, tools, agents, and more.
Event Categories
Complete Event Reference
Session Events
| Event | Fires When | Use Case |
|---|---|---|
session_start | A new session begins | Initialize state, load resources |
session_shutdown | Session is ending | Clean up, save state |
session_before_compact | Before context compaction | Customize what gets preserved |
session_compact | After context compaction | Log or process the summary |
session_before_fork | Before session fork | Validate or modify fork behavior |
session_fork | After session fork | Initialize the new session |
session_before_switch | Before switching branches | Save branch-specific state |
session_switch | After switching branches | Restore branch-specific state |
Input Events
| Event | Fires When | Use Case |
|---|---|---|
input | User submits a message | Preprocess, filter, or transform input |
Tool Events
| Event | Fires When | Use Case |
|---|---|---|
tool_call | Agent requests a tool call | Permission gating, logging, modification |
tool_result | Tool returns a result | Post-processing, validation |
execution_start | Tool execution begins | Start timers, show status |
execution_update | Tool execution progress | Update progress displays |
execution_end | Tool execution completes | Stop timers, log metrics |
Bash Events
| Event | Fires When | Use Case |
|---|---|---|
BashSpawnHook | A bash process is spawned | Sandbox configuration, env setup |
user_bash | User runs a !command | Log or gate user bash commands |
Agent/Turn Events
| Event | Fires When | Use Case |
|---|---|---|
before_agent_start | Before agent begins processing | Inject context, modify config |
agent_start | Agent starts processing | Track agent activity |
agent_end | Agent finishes processing | Collect metrics, trigger actions |
turn_start | A conversation turn begins | Per-turn setup |
turn_end | A conversation turn ends | Per-turn cleanup |
Message Events
| Event | Fires When | Use Case |
|---|---|---|
message_start | LLM begins generating | Show streaming indicators |
message_update | LLM produces a chunk | Real-time processing |
message_end | LLM finishes generating | Process complete response |
Model Events
| Event | Fires When | Use Case |
|---|---|---|
model_select | Model is changed | Log model usage, adjust settings |
context | Context is assembled for the LLM | Modify or inspect context |
Using Events in Extensions
Basic Event Handler
typescript
export default function (pi: ExtensionAPI) {
pi.on("session_start", async (event) => {
console.log("Session started:", event.sessionId);
});
pi.on("tool_call", async (event, ctx) => {
console.log(`Tool: ${event.tool}, Args: ${JSON.stringify(event.args)}`);
});
pi.on("session_shutdown", async () => {
console.log("Session ended — cleaning up");
});
}Permission Gate Example
typescript
export default function (pi: ExtensionAPI) {
const BLOCKED_COMMANDS = ["rm -rf /", "DROP DATABASE", "git push --force"];
pi.on("tool_call", async (event, ctx) => {
if (event.tool === "bash") {
const cmd = event.args.command;
for (const blocked of BLOCKED_COMMANDS) {
if (cmd.includes(blocked)) {
return {
abort: true,
message: `BLOCKED: "${blocked}" is not allowed.`
};
}
}
}
});
}Metrics Collector Example
typescript
export default function (pi: ExtensionAPI) {
const metrics = { tools: 0, tokens: 0, turns: 0 };
pi.on("tool_call", async () => { metrics.tools++; });
pi.on("turn_end", async () => { metrics.turns++; });
pi.on("message_end", async (event) => {
metrics.tokens += event.tokenCount || 0;
});
pi.on("session_shutdown", async () => {
console.log("Session metrics:", JSON.stringify(metrics, null, 2));
});
}