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.

This example builds a personal fitness coach bot on Telegram. It is the simplest complete Heypi example: no SSH tools, no approval workflows, and no admin UI. Instead it focuses on the fundamentals—a multi-skill agent folder, file-backed memory for user profiles and workout plans, a heartbeat job for daily check-ins, and three small custom tools that read and write local Markdown files.

What’s Included

Onboarding Skill

First-time setup collects goals, equipment, schedule, and constraints, then saves a structured profile with save_profile.

File-Backed Memory

state/memory/profile.md holds the user’s plan; state/memory/workouts.md accumulates workout log entries.

Daily Heartbeat

A heartbeat job fires every 24 hours. If the conversation has been idle for at least 8 hours, the agent runs the daily-checkin skill. It suppresses itself with [SILENT] on rest days.

Weekly Review

The weekly-review skill summarizes training consistency, identifies patterns, and suggests one clear focus for the next week.

Setup

1

Clone or copy the example

cp examples/telegram-workout/.env.example examples/telegram-workout/.env
2

Install dependencies

pnpm install
3

Set environment variables

Get a bot token from @BotFather in Telegram (/newbot), then edit .env:
TELEGRAM_BOT_TOKEN=<telegram-bot-token>
OPENAI_API_KEY=<openai-api-key>

# Optional comma-separated allowlists.
HEYPI_TELEGRAM_CHATS=
HEYPI_TELEGRAM_USERS=
Leave the allowlists empty for a DM-only smoke test. Set comma-separated chat or user IDs to restrict which conversations may trigger the agent.
4

Find your chat ID (optional)

To restrict the bot to a specific group or DM, run the observe command, then send a message to the bot in Telegram:
pnpm heypi telegram observe --env examples/telegram-workout/.env
Copy the printed chat ID into HEYPI_TELEGRAM_CHATS. For a DM-only setup, leave the allowlists empty.
5

Run the agent

pnpm run dev:telegram
# or directly:
node index.js
The bot uses Telegram long polling—no public URL is needed.

Configuration

const app = createHeypi({
  state: { root: stateRoot },
  adapters: [
    telegram({
      token: required("TELEGRAM_BOT_TOKEN"),
      allow: {
        chats: list("HEYPI_TELEGRAM_CHATS"),
        users: list("HEYPI_TELEGRAM_USERS"),
      },
      trigger: "mention",
      streaming: true,
    }),
  ],
  agent: agentFrom("./agent", {
    model: "openai/gpt-5-mini",
    tools: [...coreTools(), getProfile, saveProfile, logWorkout],
  }),
  jobs: [
    {
      id: "daily-workout-checkin",
      kind: "heartbeat",
      everyMs: 24 * 60 * 60 * 1000,
      idleMs: 8 * 60 * 60 * 1000,
      scope: { telegram: {} },
      prompt:
        "Use the daily-checkin skill. Review the saved profile and decide whether to check in today based on the plan, rest days, and recent context.",
    },
  ],
  runtime: { root: workspace("./workspace") },
});
The heartbeat job applies to all known Telegram chats (scope: { telegram: {} }). The idleMs: 8h setting means the agent only sends a check-in message when the conversation has been quiet for at least 8 hours—it won’t interrupt an active thread.

Custom Tools

The three custom tools in index.ts implement simple file-backed memory using Node’s fs APIs and TypeBox parameter schemas.

get_profile — Read Saved Profile

Reads state/memory/profile.md and returns it, or reports that no profile has been saved yet:
const getProfile = tool({
  name: "get_profile",
  description: "Read the saved workout profile and plan.",
  parameters: Type.Object({}),
  execute: async () => {
    try {
      return await readFile(profilePath, "utf8");
    } catch {
      return "No saved profile yet.";
    }
  },
});

save_profile — Write Profile and Plan

Overwrites state/memory/profile.md with a structured summary of the user’s goal, plan, equipment, schedule, preferences, and any constraints:
const saveProfile = tool<{
  goal: string;
  plan: string;
  equipment?: string;
  age?: number;
  weight?: string;
  schedule?: string;
  preferences?: string;
  constraints?: string;
}>({
  name: "save_profile",
  description: "Save the user's workout profile, constraints, and current plan.",
  parameters: Type.Object({
    goal: Type.String({ description: "Primary training goal." }),
    plan: Type.String({ description: "Concise current workout plan." }),
    equipment: Type.Optional(Type.String()),
    age: Type.Optional(Type.Number()),
    weight: Type.Optional(Type.String()),
    schedule: Type.Optional(Type.String()),
    preferences: Type.Optional(Type.String()),
    constraints: Type.Optional(Type.String()),
  }),
  execute: async (input) => { /* writes profile.md */ },
});

log_workout — Append a Workout Entry

Appends a single timestamped line to state/memory/workouts.md whenever the user reports a completed session:
const logWorkout = tool<{
  activity: string;
  date?: string;
  duration_min?: number;
  intensity?: string;
  notes?: string;
}>({
  name: "log_workout",
  description: "Append a completed workout entry to the local workout log.",
  parameters: Type.Object({
    activity: Type.String({ description: "Workout activity, e.g. run, lift, swim, mobility." }),
    date: Type.Optional(Type.String()),
    duration_min: Type.Optional(Type.Number()),
    intensity: Type.Optional(Type.String()),
    notes: Type.Optional(Type.String()),
  }),
  execute: async (input) => { /* appends to workouts.md */ },
});

Agent Skills

The agent/ folder contains four skills that the model selects based on conversation context:
agent/
  SOUL.md     # Concise accountability coach persona; avoids medical advice
  AGENTS.md   # Tool-use order: understand goal → save profile → log workout → next step
  skills/
    onboarding/
      SKILL.md    # Collects goals, equipment, schedule, and constraints; calls save_profile
    daily-checkin/
      SKILL.md    # Heartbeat-triggered; reads profile; sends [SILENT] on rest days
    daily-workout/
      SKILL.md    # Reflects on today's session; infers completed work and next action
    weekly-review/
      SKILL.md    # Summarizes the week; identifies patterns; suggests next focus

onboarding

Triggered when the user is new or hasn’t shared enough context. Asks for goal, equipment, schedule, and constraints, then calls save_profile.

daily-checkin

Used by the heartbeat job. Reads the saved profile, checks whether today is a training or rest day, and either asks a short accountability question or returns [SILENT].

daily-workout

Handles real-time workout reports—what the user did, how it felt, and what the next step is. Calls log_workout to persist the entry.

weekly-review

Summarizes training across the week, identifies the most useful pattern or blocker, and suggests one clear focus for the next week.

Heartbeat Job

The daily check-in is configured as a kind: "heartbeat" job, which differs from a cron job in one important way: instead of firing at a fixed wall-clock time, it fires on a rolling interval relative to the last activity in each conversation scope.
{
  id: "daily-workout-checkin",
  kind: "heartbeat",
  everyMs: 24 * 60 * 60 * 1000,  // check every 24 hours
  idleMs: 8 * 60 * 60 * 1000,    // only if idle for 8+ hours
  scope: { telegram: {} },         // applies to all known Telegram chats
  prompt: "Use the daily-checkin skill. Review the saved profile and decide whether to check in today ...",
}
  • everyMs — how often Heypi evaluates each in-scope thread.
  • idleMs — the minimum quiet window required before the agent will send a message.
  • scope{ telegram: {} } means the job applies to every chat that has had at least one message.
The daily-checkin skill instructs the agent to return [SILENT] when there is no useful reason to interrupt—for example, on a planned rest day. Heypi suppresses messages that contain only [SILENT].

Try It Out

I want to get stronger and lose 10 pounds. I have dumbbells and can train Monday, Wednesday, Friday.
I ran 35 minutes easy today
I skipped legs again this week
Can you review my week?

Build docs developers (and LLMs) love