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

Make Slack responses concise

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:
export default function toolPolicy(pi) {
  pi.on('before_agent_start', async (_event, ctx) => {
    if (ctx.external?.conversationType === 'channel') {
      await pi.setActiveTools(['read', 'grep', '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

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

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

GoalHook
Modify the prompt or select model/toolsbefore_agent_start
Allow or block a tool calltool_call
Transform a tool resulttool_result
Transform model context messagescontext
Adjust provider stream optionsbefore_provider_request
Inspect or replace provider payloadbefore_provider_payload
Observe provider response metadataafter_provider_response
Participate in compactionsession_before_compact, session_compact
Observe model or thinking changesmodel_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 for every supported event and return contract.