Skip to main content

Customization

The Operating System

The operating_system/ directory is the agent's brain — it defines who the agent is and how it behaves.

FilePurpose
SOUL.mdAgent identity, personality traits, and values
CHATBOT.mdSystem prompt for Telegram chat
JOB_SUMMARY.mdPrompt for summarizing completed jobs
HEARTBEAT.mdSelf-monitoring behavior
CRONS.jsonScheduled job definitions
TRIGGERS.jsonWebhook trigger definitions

Each job automatically gets its own logs/<JOB_ID>/job.md file created by the event handler. Jobs are created via Telegram chat, webhooks, or cron schedules.


Using Your Bot

There are currently two ways to trigger jobs — Telegram and webhooks — with more integrations coming soon.

Telegram Chat

Message your bot directly to chat or create jobs. The bot uses Claude to understand your requests and can:

  • Chat - Have a conversation, ask questions, get information
  • Create jobs - Say "create a job to..." and the bot will spawn an autonomous agent

Security: During setup, you'll verify your chat ID. Once configured, the bot only responds to messages from your authorized chat and ignores everyone else.

Voice Messages

Send voice notes to your bot and they'll be transcribed using OpenAI Whisper.

Requirements:

  • OPENAI_API_KEY in your .env file

The bot automatically detects voice messages and transcribes them before processing.

Webhooks

Create jobs programmatically via HTTP:

curl -X POST http://localhost:3000/webhook \
-H "Content-Type: application/json" \
-H "x-api-key: YOUR_API_KEY" \
-d '{"job": "Update the README with installation instructions"}'

Scheduled Jobs

Define recurring jobs in operating_system/CRONS.json:

[
{
"name": "daily-check",
"schedule": "0 9 * * *",
"job": "Check for dependency updates",
"enabled": true
}
]

Set "enabled": true to activate a scheduled job.


Skills

Add custom skills for the agent in .pi/skills/. Skills extend the agent's capabilities with specialized tools and behaviors.


Security

What the AI triesWhat happens
echo $ANTHROPIC_API_KEYEmpty
echo $GH_TOKENEmpty
cat /proc/self/environSecrets missing
Claude API callsWork normally
GitHub CLI commandsWork normally

How Secret Protection Works

  1. GitHub passes a single SECRETS env var (base64-encoded JSON with all credentials)
  2. The entrypoint decodes the JSON and exports each key as an env var
  3. Pi starts - SDKs read their env vars (ANTHROPIC_API_KEY, gh CLI uses GH_TOKEN)
  4. The env-sanitizer extension filters ALL secret keys from bash subprocess env
  5. The LLM can't echo $ANYTHING - subprocess env is filtered
  6. Other extensions still have full process.env access

What's Protected:

Any key in the SECRETS JSON is automatically filtered from the LLM's bash environment. The SECRETS variable itself is also filtered.

# If your SECRETS contains:
{"GH_TOKEN": "...", "ANTHROPIC_API_KEY": "...", "MY_CUSTOM_KEY": "..."}

# Then all of these return empty:
echo $GH_TOKEN # empty
echo $ANTHROPIC_API_KEY # empty
echo $MY_CUSTOM_KEY # empty

LLM-Accessible Secrets

Sometimes you want the LLM to have access to certain credentials - browser logins, skill API keys, or service passwords. Use LLM_SECRETS for these.

# Protected (filtered from LLM)
SECRETS=$(echo -n '{"GH_TOKEN":"ghp_xxx","ANTHROPIC_API_KEY":"sk-ant-xxx"}' | base64)

# Accessible to LLM (not filtered)
LLM_SECRETS=$(echo -n '{"BROWSER_PASSWORD":"mypass123"}' | base64)
Credential TypePut InWhy
GH_TOKENSECRETSAgent shouldn't push to arbitrary repos
ANTHROPIC_API_KEYSECRETSAgent shouldn't leak billing keys
Browser login passwordLLM_SECRETSSkills may need to authenticate
Third-party API key for a skillLLM_SECRETSSkills need these to function

Implementation

The env-sanitizer extension in .pi/extensions/ dynamically filters secrets:

const bashTool = createBashTool(process.cwd(), {
spawnHook: ({ command, cwd, env }) => {
const filteredEnv = { ...env };
if (process.env.SECRETS) {
try {
for (const key of Object.keys(JSON.parse(process.env.SECRETS))) {
delete filteredEnv[key];
}
} catch {}
}
delete filteredEnv.SECRETS;
return { command, cwd, env: filteredEnv };
},
});

No special Docker flags required. Works on any host.

Custom Extensions

The env-sanitizer protects against the AI agent accessing secrets through bash. Extension code itself can access process.env directly - this is by design.

Best practices:

  • Don't create tools that echo environment variables to the agent
  • Review extension code before adding to your agent