Browse documentation
Build channel-aware agents
Adapt agent behavior to Slack, Microsoft Teams, and API conversations without exposing provider credentials.
View MarkdownSalambo delivers message text as the normal agent input. For integration-triggered turns, tools and hooks also receive bounded channel metadata in ctx.external.
Branch by provider
export default function channelPolicy(pi) {
pi.on("before_agent_start", (event, ctx) => {
const external = ctx.external;
if (!external) {
return;
}
const instruction =
external.provider === "slack"
? "Use short paragraphs suitable for Slack."
: external.provider === "microsoft_teams"
? "Use a concise Teams-friendly response."
: "Use a concise chat response.";
return {
systemPrompt: `${event.systemPrompt}\n\n${instruction}`,
};
});
}For API, playground, scheduled, or other non-channel turns, ctx.external is null.
Use conversation identity in a tool
pi.registerTool({
name: "current_conversation",
description: "Return the current external conversation identity.",
parameters: {
type: "object",
properties: {},
additionalProperties: false,
},
async execute(_id, _params, _signal, _update, ctx) {
if (!ctx.external) {
return {
content: [
{ type: "text", text: "This turn is not from a chat integration." },
],
};
}
return {
content: [
{
type: "text",
text: JSON.stringify({
provider: ctx.external.provider,
conversationId: ctx.external.conversationId,
conversationType: ctx.external.conversationType,
}),
},
],
};
},
});Security boundary
ctx.external contains identifiers and routing metadata. It does not contain:
- Slack or Teams access tokens;
- signing secrets or client secrets;
- provider credentials;
- the message text;
- arbitrary raw webhook bodies.
Treat identifiers as customer metadata. Do not publish them in artifacts or external logs unless the product requires it.
See the exact external event context, then choose a channel-aware recipe.