# Channel-aware agent recipes

Practical extension patterns for Slack and Microsoft Teams conversations.

These recipes use `ctx.external` while keeping message transport, authentication, deduplication, and replies owned by Salambo.

## Slack thread-aware instructions

```js
export default function slackBehavior(pi) {
  pi.on('before_agent_start', (event, ctx) => {
    const external = ctx.external;

    if (external?.provider !== 'slack') {
      return;
    }

    const location = external.refs.threadTs
      ? 'an existing Slack thread'
      : 'a new Slack conversation';

    return {
      systemPrompt: `${event.systemPrompt}\n\nThis request came from ${location}. Reply concisely and do not repeat the full question.`,
    };
  });
}
```

## Teams channel-aware instructions

```js
export default function teamsBehavior(pi) {
  pi.on('before_agent_start', (event, ctx) => {
    const external = ctx.external;

    if (external?.provider !== 'microsoft_teams') {
      return;
    }

    const audience = external.refs.channelId
      ? 'a Teams channel'
      : 'a direct Teams conversation';

    return {
      systemPrompt: `${event.systemPrompt}\n\nThe audience is ${audience}. Keep the answer clear for everyone in that conversation.`,
    };
  });
}
```

## Restrict a sensitive tool to direct messages

```js
pi.on('tool_call', (event, ctx) => {
  if (event.toolName !== 'lookup_private_customer_record') {
    return;
  }

  if (ctx.external && ctx.external.conversationType !== 'dm') {
    return {
      block: true,
      reason:
        'Private customer records can only be queried from a direct message.',
    };
  }
});
```

Decide separately whether API-originated turns (`ctx.external === null`) are allowed. The example allows them.

## Attach routing metadata to tool details

```js
async function execute(_id, params, _signal, _update, ctx) {
  const external = ctx.external;

  return {
    content: [{ type: 'text', text: 'Request recorded.' }],
    details: {
      requestId: params.requestId,
      provider: external?.provider ?? null,
      conversationId: external?.conversationId ?? null,
    },
  };
}
```

Do not copy provider tokens, complete messages, or unnecessary sender identifiers into tool results.

## Preserve transport ownership

Extensions should adapt agent behavior, not send Slack or Teams replies
directly. Salambo owns webhook verification, idempotency, conversation mapping,
turn creation, and provider delivery.

For setup, use the [Slack guide](/docs/guides/slack-integration) or [Microsoft Teams guide](/docs/guides/microsoft-teams-integration).
