# React to lifecycle events

Adapt prompts, tools, model behavior, and results with supported hosted hooks.

Use `pi.on(eventName, handler)` when behavior belongs around the agent lifecycle rather than inside a model-callable tool.

## Make Slack responses concise

```js
export default function channelBehavior(pi) {
  pi.on('before_agent_start', (event, ctx) => {
    if (ctx.external?.provider !== 'slack') {
      return;
    }

    return {
      systemPrompt: `${event.systemPrompt}\n\nFor Slack, keep the response concise and easy to scan.`,
    };
  });
}
```

All hosted handlers receive `(event, ctx)`. `event` depends on the hook. `ctx.external` contains bounded integration metadata or `null`.

## Select tools for a turn

Call runtime-changing methods inside a handler, not at module discovery time:

```js
export default function toolPolicy(pi) {
  pi.on('before_agent_start', async (_event, ctx) => {
    if (ctx.external?.conversationType === 'channel') {
      await pi.setActiveTools(['read', 'bash', 'lookup_customer']);
    }
  });
}
```

The requested names must belong to the deployment's allowed tool set. Salambo restores the selected tools on durable follow-up turns.

## Block a tool call

```js
pi.on('tool_call', (event) => {
  if (
    event.toolName === 'bash' &&
    typeof event.input.command === 'string' &&
    event.input.command.includes('rm -rf')
  ) {
    return {
      block: true,
      reason: 'Destructive recursive deletion is not allowed.',
    };
  }
});
```

`tool_call` handlers may block a call. They may also mutate `event.input` in place; later handlers and execution receive the resulting input.

## Annotate a tool result

```js
pi.on('tool_result', (event) => {
  if (event.toolName !== 'lookup_customer' || event.isError) {
    return;
  }

  return {
    details: {
      ...event.details,
      reviewedBy: 'customer-policy-extension',
    },
  };
});
```

## Choose a hook deliberately

| Goal                                    | Hook                                        |
| --------------------------------------- | ------------------------------------------- |
| Modify the prompt or select model/tools | `before_agent_start`                        |
| Allow or block a tool call              | `tool_call`                                 |
| Transform a tool result                 | `tool_result`                               |
| Transform model context messages        | `context`                                   |
| Adjust provider stream options          | `before_provider_request`                   |
| Inspect or replace provider payload     | `before_provider_payload`                   |
| Observe provider response metadata      | `after_provider_response`                   |
| Participate in compaction               | `session_before_compact`, `session_compact` |
| Observe model or thinking changes       | `model_update`, `thinking_level_update`     |

Hooks are bounded to 10 seconds. A timeout, thrown error, or invalid result fails the active extension operation rather than silently ignoring it.

See the authoritative [hook event reference](/docs/reference/extensions/hooks) for every supported event and return contract.
