Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/hunvreus/heypi/llms.txt

Use this file to discover all available pages before exploring further.

Heypi normalizes Slack, Telegram, and Discord events into a shared processing pipeline. After a provider-specific allow check and trigger check, all three platforms follow the same behavior for streaming, progress messages, busy threads, approvals, and delivery. This page covers that shared behavior.

Access and Triggers

The allow config filters events after the provider delivers them to heypi. Provider setup (tokens, scopes, webhooks) decides which events arrive at all.
  • Omitting allow accepts all delivered events.
  • Omitting team/channel/user lists accepts all events for that dimension.
  • dms defaults to true; set dms: false to drop direct messages.
  • In top-level groups and channels, trigger defaults to "mention" — the agent only responds when mentioned.
  • Set trigger: "message" to run the agent for every allowed group/channel message.
  • Accepted DMs always run the agent regardless of trigger.
  • Thread/topic replies default to message-triggered follow-ups.
  • Set threadTrigger: "mention" to require a mention in thread or topic replies.
allow.users controls who may talk to the bot. approval.approvers controls who may approve tool calls. These are separate lists with separate semantics.

Streaming

Streaming is opt-in and configured per adapter:
slack({
  // ...
  streaming: true,
});
Use true for sensible defaults, or pass an options object to fine-tune:
slack({
  streaming: {
    intervalMs: 1500,    // how often to push draft edits
    minChars: 50,        // minimum new characters before pushing
    maxFailures: 3,      // stop streaming after this many send failures
  },
});

Progress Messages

While the agent is working, heypi shows a Working... progress message. This can be disabled:
slack({ progress: false });
Posts the progress message while streaming. Top-level channel messages also get the configured reaction. Confirmed tool calls stop draft streaming before the approval card is sent.

Busy Threads

When a new message arrives while a turn is already active in the same thread, heypi uses the chat.busy policy to decide what to do:

steer

Default. Inject the new message into the active Pi session so it can influence the ongoing response.

followUp

Queue the message to be handled after the current turn completes.

reject

Ask the user to send it again later.
For steer and followUp, heypi:
  1. Stores the inbound message.
  2. Publicly acknowledges it in the thread.
  3. Keeps the original progress marker as a temporary anchor.
  4. Deletes the progress marker at turn completion.
  5. Posts the final answer at the bottom of the thread.
Pending approvals always reject new asks, regardless of chat.busy. A turn waiting for an approval will tell the user to resolve the approval first.

System Messages

Bot outcome messages are fully configurable with top-level messages. Provider labels, card headings, and button text are fixed.
createHeypi({
  state: { root: "./state" },
  // ...adapters, agent, runtime
  chat: { busy: "steer" },
  messages: {
    busySteer:              "Got it. I'll include that.",
    busyFollowUp:           "Got it. I'll handle that next.",
    busyReject:             "I'm still working on the previous message. Send this again after I reply, or use `cancel`.",
    pendingApprovalReject:  "I'm waiting for the pending approval first.",
    approvalUnavailable:    "Approval unavailable. Ask me to try again if this is still needed.",
    approvalAlreadyResolved: ({ state, resolvedBy }) =>
      `Approval already ${state} by ${resolvedBy ?? "unknown"}.`,
    approvalResolved:       "Approval already resolved.",
    approvalExpired:        "Approval expired. Ask me to try again if this is still needed.",
    approvalUnauthorized:   "You are not allowed to resolve this action.",
    cancelled:              "Cancelled.",
    cancelUnauthorized:     "You are not allowed to cancel this run.",
    cancelNotFound:         "No active run found for that id.",
    approvalsUnauthorized:  "You are not allowed to view pending approvals.",
    runtimeStarting:        "Preparing runtime...", // set false to keep default progress text
    runtimeFailed:          "Runtime failed. Ask an admin to check the server logs.",
    error:                  "Something went wrong. Ask an admin to check the server logs.",
  },
});

Approvals and Text Commands

Slack, Telegram, and Discord render provider-native approval buttons. Approve/deny actions edit the original approval card in place and remove the buttons. Stale approval buttons also update in place with an unavailable or already-resolved notice. Text commands work in every adapter, including webhook:
approvals                  — list pending approvals (thread-scoped or global for approvers)
approve <approval-id>      — approve a pending tool call
deny <approval-id>         — deny a pending tool call
status                     — show the status of the current turn
status <call-id>           — show the status of a specific call
cancel <turn-id>           — cancel an active turn

Permissions

ActionWho can perform it
approveConfigured approval.approvers, or any user if none are configured
denyConfigured approvers, or the original requester
cancelRun initiator, or configured approvers
approvalsConfigured approvers; or current-thread users when no approvers are configured
Unauthorized button actions receive private provider-native feedback where the provider supports it.

Approval Cards

Approval cards show the message (user-facing reason), optional structured details, and the requester’s name. details are label/value pairs; format: "code" renders as a code block where the provider supports it. Details are capped to stay within provider card limits.

Delivery

Adapter sends are serialized by default. Provider rate limits are retried with backoff per adapter instance. Ambiguous timeouts are not retried for non-idempotent sends such as new messages or file uploads.
createHeypi({
  // ...
  delivery: {
    intervalMs: 500,   // pacing between sends; raise if the provider needs slower pacing
  },
});
Set delivery: false only for development or when you are implementing a fully custom transport. Normal deployments should keep the default serialized delivery behavior.

Build docs developers (and LLMs) love