An extensible personal assistant framework for interacting with AI agents through various communication channels.
Poppy is a modular personal assistant that connects communication channels (like iMessage) with AI agents to automate tasks and provide intelligent assistance. Built with TypeScript and deployed on Cloudflare Workers, it uses a task-based system to run agents in the background, on triggers, or as scheduled jobs.
- iMessage - Send and receive messages via Loop Message API with webhook integration
- Voice - Voice-based interactions
- SMS - Direct SMS integration
- WhatsApp - WhatsApp Business API
Agents are configured via a task-based system that allows them to run in the background, on triggers/hooks, or as cron jobs.
- Reservation Finding - Search for restaurant availability across multiple platforms
- Reservation Booking - Complete restaurant bookings
- Calendar - Manage calendar events and scheduling
- Email - Email management and automation
- Travel - Flight and hotel bookings
- Task Management - Todo lists and reminders
- Runtime: Cloudflare Workers
- Package Manager: pnpm with workspaces
- Build System: Turborepo
- Backend: Hono (TypeScript)
- Database: PostgreSQL with Drizzle ORM (hosted on Supabase, connected via Hyperdrive)
- Stateful Storage: Durable Objects
- Validation: Zod schemas
- Code Quality: Biome for linting and formatting
- Messaging: Loop Message API
- AI: OpenAI and OpenRouter
- Cloudflare account with Workers enabled
- pnpm v10.12.1 or higher
- PostgreSQL database (Supabase recommended)
- Loop Message API account for iMessage integration
- Cloudflare API token with Workers permissions
# Clone the repository
git clone https://github.com/yourusername/poppy.git
cd poppy
# Install dependencies
pnpm install
# Authenticate with Cloudflare
wrangler login# Run the worker locally with hot reload
cd apps/interaction-worker
pnpm dev
# Run type checking
pnpm typecheck
# Run linting with Biome (from root)
cd ../..
pnpm lint
# Format code
pnpm format
# Build all packages
pnpm build# Navigate to database package
cd packages/db
# Generate migration files
pnpm db:generate
# Apply migrations to database
pnpm db:migrate
# Push schema changes (development only)
pnpm db:push
# Open Drizzle Studio to view database
pnpm db:studio
# Seed database with initial data (optional)
pnpm db:seed
# Reset database (Supabase only)
pnpm db:resetPoppy is designed to run exclusively on Cloudflare Workers, providing a serverless, globally distributed architecture with Durable Objects for message debouncing and Hyperdrive for PostgreSQL connectivity.
- Install Wrangler globally (optional):
npm install -g wrangler- Authenticate with Cloudflare:
wrangler login- Configure Hyperdrive for database connectivity:
# Create a Hyperdrive connection to your PostgreSQL database
wrangler hyperdrive create production-db --connection-string="postgresql://user:password@host:5432/database"Update the Hyperdrive ID in apps/interaction-worker/wrangler.jsonc under the production or staging environment with the ID returned from the command above.
- Configure Durable Objects:
The worker uses Durable Objects for message debouncing. These are automatically deployed with the worker, but you need to ensure migrations are applied:
cd apps/interaction-worker
wrangler deployments list # Verify migrations are appliedConfigure required secrets using Wrangler:
cd apps/interaction-worker
# Loop Message API credentials
pnpm wrangler secret put LOOP_AUTHORIZATION_KEY --env production
pnpm wrangler secret put LOOP_SECRET_KEY --env production
# AI API keys
pnpm wrangler secret put OPENAI_API_KEY --env production
pnpm wrangler secret put OPENROUTER_API_KEY --env production
pnpm wrangler secret put EXASEARCH_API_KEY --env productionFor staging environment, replace --env production with --env staging.
# Deploy to production
cd apps/interaction-worker
pnpm deploy
# Deploy to staging
pnpm deploy:staging
# Or from the root directory using Turborepo
pnpm turbo deployThe worker is configured via apps/interaction-worker/wrangler.jsonc with two environments:
- Production:
poppy-interaction-production.caelinsutch.workers.dev - Staging:
poppy-interaction-staging.caelinsutch.workers.dev
Each environment has its own:
- Durable Object bindings for message debouncing
- Hyperdrive connection for database access
- Environment variables and secrets
The project includes a GitHub Actions workflow (.github/workflows/deploy.yml) that automatically deploys to production when changes are pushed to the main branch.
To enable automatic deployments:
-
Add GitHub secrets:
CLOUDFLARE_ACCOUNT_ID- Your Cloudflare account IDCLOUDFLARE_API_TOKEN- API token with Workers deploy permissions
-
Push to main branch:
git push origin mainThe workflow will automatically build and deploy the worker to production.
View worker logs and traces:
# Tail production logs
wrangler tail --env production
# Tail staging logs
wrangler tail --env stagingLogs and traces are automatically persisted in Cloudflare's observability dashboard with 100% sampling rate configured in wrangler.jsonc.
To use a custom domain:
- Add a custom domain in Cloudflare Workers dashboard
- Update the worker route configuration
- Configure your DNS records
wrangler publish --env productionpoppy/
├── apps/
│ ├── interaction-worker/ # Main Cloudflare Worker for message handling
│ │ ├── src/
│ │ │ ├── clients/ # API clients (Loop Message, AI providers)
│ │ │ ├── durable-objects/ # Durable Object implementations
│ │ │ ├── tools/ # AI agent tools (web search, etc.)
│ │ │ └── index.ts # Worker entry point
│ │ ├── wrangler.jsonc # Cloudflare Workers configuration
│ │ └── package.json
│ └── execution-worker/ # Background task execution worker
│ ├── src/
│ │ ├── clients/ # API clients
│ │ └── index.ts # Worker entry point
│ ├── wrangler.jsonc # Cloudflare Workers configuration
│ └── package.json
├── packages/
│ ├── clients/ # Shared API client implementations
│ ├── db/ # Drizzle ORM schema and utilities
│ ├── hono-helpers/ # Hono middleware and helpers
│ ├── lib/ # Shared utility functions
│ ├── schemas/ # Zod validation schemas
│ ├── tools/ # Build and deployment scripts
│ ├── types/ # TypeScript type definitions
│ ├── supabase/ # Supabase migrations and configuration
│ └── typescript-config/ # Shared TypeScript configurations
├── .github/
│ └── workflows/
│ ├── ci.yml # Continuous integration workflow
│ └── deploy.yml # Deployment workflow
├── turbo.json # Turborepo configuration
├── pnpm-workspace.yaml # pnpm workspace configuration
├── biome.json # Biome linting/formatting config
└── CLAUDE.md # AI assistant development guide
Poppy runs entirely on Cloudflare's edge infrastructure:
- Serverless, globally distributed: Deployed across Cloudflare's network for low latency worldwide
- Durable Objects: Stateful coordination for message debouncing and deduplication
- Hyperdrive: Low-latency database connections to PostgreSQL
- Automatic scaling: Handles variable workloads without manual intervention
- Cost-effective: Pay only for what you use
- Interaction Worker: Handles incoming webhooks from Loop Message, processes messages with AI agents, and manages conversations
- Execution Worker: Runs background tasks and scheduled jobs for agents
- Webhook Reception: The interaction worker receives webhooks at
/api/webhooks/loop-messagefor message events (inbound, sent, failed, reactions, etc.) - Debouncing: Messages are deduplicated using Durable Objects to handle rapid message updates
- Validation: Webhooks are validated using Zod schemas from
@poppy/schemas - AI Processing: Messages are processed by AI agents with access to various tools (web search, etc.)
- Storage: Messages and interactions are stored in PostgreSQL via Drizzle ORM (through Hyperdrive)
- Loop Message API: Handles iMessage communication with webhook-based event handling
- Database Layer: Drizzle ORM provides type-safe database access with PostgreSQL (via Hyperdrive on Workers)
- AI Providers: OpenAI and OpenRouter for language model capabilities
- Durable Objects: Stateful coordination for message debouncing and deduplication
- Validation Layer: Zod schemas ensure data integrity across the application
- Monorepo Structure: Turborepo enables efficient builds and shared packages
For local development, create apps/interaction-worker/.env based on .env.production.example.
For production deployment, secrets are configured via Wrangler (see Deployment section above).
Environment variables set in wrangler.jsonc:
NODE_ENV- Environment (staging/production)NAME- Worker name for identification
Secrets (set via wrangler secret put):
LOOP_AUTHORIZATION_KEY- Loop Message API authorization keyLOOP_SECRET_KEY- Loop Message API secret keyOPENAI_API_KEY- OpenAI API keyOPENROUTER_API_KEY- OpenRouter API keyEXASEARCH_API_KEY- Exa Search API key
This project uses Biome for linting and formatting with the following conventions:
- Double quotes for strings
- 2-space indentation
- Trailing commas in multi-line structures
- Semicolons required
- Line width: 80 characters
- Strict TypeScript type checking
Run pnpm lint to check and pnpm format to auto-format code.
- Fork the repository
- Create a feature branch (
git checkout -b feature/amazing-feature) - Make your changes following the code style guidelines
- Run tests and type checking (
pnpm typecheck) - Commit your changes (
git commit -m 'Add amazing feature') - Push to the branch (
git push origin feature/amazing-feature) - Open a Pull Request
GNU General Public License v3.0