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 gives your agent a curated set of runtime-backed tools for shell, files, and search, and lets you add your own. Every tool call can be gated behind a human approval step: heypi pauses the turn, sends an approval card to your chat platform, and replays the call only after a human says yes. Approvals are first-class — persisted in SQLite, rendered with native buttons on Slack, Telegram, and Discord, and resolvable by text command on every adapter.

Built-in Core Tools

Heypi registers the following Pi-compatible tools automatically:

bash

Run shell commands. Requires approval by default via commandConfirm().

read

Read a file from the runtime workspace.

write

Write a file to the runtime workspace.

edit

Edit (patch) a file in the runtime workspace.

grep

Search file contents with a regex pattern.

find

List files matching a glob pattern.

ls

List directory entries in the runtime workspace.

history

Read thread message history.
bash uses commandConfirm() by default. File and search tools (read, write, edit, grep, find, ls) run inside the scoped runtime workspace with path containment and size limits enforced. write and edit do not require approval unless you configure one.

coreTools() API

Use coreTools() to include and selectively configure the built-in tools. Pass false to disable a tool, true to enable it without a custom confirm, or an options object to override its confirmation:
import { agentFrom, commandConfirm, coreTools } from "@hunvreus/heypi";

agentFrom("./agent", {
  model: "openai/gpt-5-mini",
  tools: [
    ...coreTools({
      bash: {
        confirm: commandConfirm({
          allow: [/^curl -I https:\/\/status\.example\.com\b/],
          approve: [/\bmake deploy\b/],
          block: [/\bgh repo delete\b/],
        }),
      },
      write: false,
      edit: false,
    }),
    myTool,
  ],
});
Omitting tools entirely uses the full default core tool set with commandConfirm() on bash. If you pass tools, include coreTools() explicitly or those tools will not be available.

Custom Tools with tool()

Use tool() when your custom tool needs a confirmation step. Heypi stores the pending call, renders approval buttons where the adapter supports them, and can replay the call safely after approval.
import { tool } from "@hunvreus/heypi";
import { Type } from "@sinclair/typebox";

const pageService = tool<{ service: string; reason: string }>({
  name: "page_service",
  description: "Record a service page request.",
  parameters: Type.Object({
    service: Type.String(),
    reason: Type.String(),
  }),
  confirm: ({ service, reason }) => ({
    message: "Page service.",
    details: [
      { label: "Service", value: service },
      { label: "Reason", value: reason },
    ],
  }),
  execute: async ({ service, reason }) =>
    `page recorded: service=${service} reason=${reason}`,
});
Parameters are described using TypeBox schemas, which produce JSON Schema at runtime.

The confirm Function

The confirm field controls whether a call requires human approval. It can be a static object or a function that receives the tool arguments:
Return { message, details } to pause the turn and request approval.
confirm: ({ ticket }) => ({
  message: `Delete ticket ${ticket}.`,
  details: [{ label: "Ticket", value: ticket }],
})

policyReason (Audit-Only)

The ConfirmResult type also accepts a policyReason field. Unlike message, it is not shown as the main approval text in cards or buttons — it is recorded for policy audit purposes only. Use it to capture the rule or pattern that triggered approval (for example, the regex that matched):
confirm: ({ command }) => ({
  message: "Run bash command.",
  policyReason: "approval by /\\bcurl\\b/i", // audit trail only, not shown to approvers
  details: [{ label: "Command", value: command, format: "code" }],
})

Approval details

Each entry in details is a label/value pair shown in the approval card:
details: [
  { label: "Target", value: "prod-web-1" },
  { label: "Command", value: "systemctl restart app", format: "code" },
]
FieldTypeNotes
labelstringDisplay label shown in the approval card
valuestringThe field value
format"text" | "code"Optional. Use "code" to render as a code block
Keep detail values concise — provider message limits apply.

Approval Configuration

Configure who can approve tool calls and how long approvals stay open at the app level:
createHeypi({
  // ...
  approval: {
    approvers: ["U123456", "U789012"],
    expiresInMs: 10 * 60 * 1000, // 10 minutes
  },
});
An empty or omitted approvers list means any user in that chat can approve.

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 get private provider-native feedback where the provider supports it.

commandConfirm() for Bash Risk

commandConfirm() translates command risk classification into the tool confirm contract. It parses the shell command, classifies each segment, and returns false (allow), an approval prompt, or a block based on your rules.
import { commandConfirm } from "@hunvreus/heypi";

commandConfirm({
  allow:   [/^curl -I https:\/\/status\.example\.com\b/],
  approve: [/\bmake deploy\b/],
  block:   [/\bgh repo delete\b/],
});

Evaluation Order

Each parsed command segment is classified independently, and the highest risk wins:
1

Custom block

If any block pattern matches → block
2

Built-in hard blocks

Patterns like rm -rf /, mkfs, shutdown, rebootblock
3

Custom allow

If any allow pattern matches → allow (bypasses approval patterns for that segment)
4

Custom approve

If any approve pattern matches → request approval
5

Built-in approval patterns

Patterns like curl, wget, ssh, scp, rsync, docker, kubectl, terraform, helm, firewall changes (ufw, firewall-cmd, iptables, nft), git push, npm publish, pnpm publish, and rm -rf (without a trailing /) → request approval
6

Default

No match → allow
commandConfirm() is a governance guardrail, not an OS sandbox. Use just-bash (the default runtime) for actual filesystem and network isolation in team-facing agents.
If the shell cannot be parsed, classification fails closed and requires approval. classifyCommand() is exported for lower-level use when you need to inspect the CommandRisk result directly. Most code should use commandConfirm() instead, which wraps classification into the tool confirm contract.

Using ctx.runtime in Custom Tools

Custom tool execute functions run as trusted Node.js code. When a tool needs to run commands or file operations inside the configured runtime sandbox, use ctx.runtime:
import { tool } from "@hunvreus/heypi";
import { Type } from "@sinclair/typebox";

const inspect = tool({
  name: "inspect",
  description: "Inspect the runtime workspace.",
  parameters: Type.Object({}),
  execute: async (_params, ctx) => {
    const result = await ctx.runtime.bash?.({
      command: "pwd && ls",
      signal: ctx.signal,
    });
    return result?.out ?? "runtime does not support bash";
  },
});
ctx.runtime is the scoped runtime bound to the current turn. Calls through it are covered by the custom tool’s own confirm decision — heypi does not create separate nested approval records for those internal calls.

Approval Cards and Text Commands

Slack, Telegram, and Discord render provider-native approval buttons. The card shows the message, optional details, and the requester’s name. Approve/deny actions edit the original message in place and remove the buttons. Text commands work in every chat adapter:
approvals                  — list pending approvals in this thread
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

Build docs developers (and LLMs) love