Skip to content

Spkap/imessage-copilot

Repository files navigation

iMessage Copilot

Your AI copilot that lives in iMessage — morning briefings, restaurant picks, goal tracking, and it remembers everything.

Built on Photon’s iMessage stack for the Photon residency bar: typed, resilient, and fast enough to feel like texting instead of waiting on an app.

What it handles

Intent Example Behavior
Morning briefing gm Weather, calendar, and current priority in one message
Restaurant pick cozy ramen spot in the mission for 2 One confident recommendation with a Maps link
Goal capture I want to journal every night Creates a goal and schedules nightly check-ins
Check-in replies yes did it Updates streaks and responds with encouragement
Progress how am I doing Concise progress summary instead of a raw data dump
General chat what's the weather Falls through to the chat agent when it is not a briefing
Safety / control help, forget me, gibberish Capability list, memory wipe, or graceful fallback

Core design

  • A three-stage router handles obvious intents without paying for LLM calls, then falls back to LLM classification only when necessary.
  • Every service boundary uses typed interfaces, Result<T, E> return values, retries, timeouts, and circuit breakers where external calls can fail.
  • Message batching, deduplication, per-user spend protection, and graceful shutdown are built into the runtime path rather than layered on later.

Architecture

flowchart LR
  Entry[index.ts] --> Config[config/config.ts]
  Entry --> Container[src/container.ts]
  Entry --> Handler[src/router/message-handler.ts]
  Handler --> Router[src/router/intent-router.ts]
  Handler --> Agents[src/agents/*]
  Agents --> Services[src/services/*]
  Agents --> Memory[src/memory/*]
  Services --> Utils[src/utils/*]
  Memory --> Utils
Loading

Main runtime flow:

  1. Validate configuration at startup with Zod.
  2. Build the DI container and warm caches / stores.
  3. Start the scheduler for accountability check-ins.
  4. Start the Photon iMessage watcher.
  5. Route inbound messages through batching, dedup, intent resolution, and the selected agent.

Stack

  • Runtime: Bun
  • Language: TypeScript with branded domain types
  • Transport: Photon @photon-ai/imessage-kit
  • Memory / goals: SQLite
  • LLMs: OpenAI, Gemini, or Anthropic Claude
  • External data: OpenWeatherMap, Google Places, macOS Calendar

Project layout

.
├── index.ts
├── config/
│   └── config.ts
├── src/
│   ├── agents/
│   ├── container.ts
│   ├── memory/
│   ├── router/
│   ├── services/
│   ├── types/
│   └── utils/
└── tests/
    ├── fakes/
    └── unit/

Important directories:

  • src/agents: briefing, restaurant, accountability, and general chat behavior.
  • src/services: adapters and resilience wrappers for LLM, weather, places, calendar, scheduler, and goal extraction.
  • src/memory: persistent conversation memory, preferences, user location, and goal state.
  • src/utils: batching, caching, retry, timeout, circuit breaker, logging, parsing, rate limiting, and dedup.

Configuration

Copy .env.example to .env.

Variable Required Notes
LLM_PROVIDER no openai, gemini, or anthropic — defaults to gemini
OPENAI_API_KEY if openai OpenAI API key
OPENAI_MODEL no Defaults to gpt-4o
OPENAI_MAX_TOKENS no Defaults to 512
GOOGLE_AI_API_KEY if gemini Google AI Studio key
GEMINI_MODEL no Defaults to gemini-3-flash-preview
GEMINI_MAX_TOKENS no Defaults to 512
ANTHROPIC_API_KEY if anthropic Anthropic API key
ANTHROPIC_MODEL no Defaults to claude-sonnet-4-6
ANTHROPIC_MAX_TOKENS no Defaults to 512
OPENWEATHER_API_KEY no Enables weather in briefings (optional)
GOOGLE_PLACES_API_KEY no Enables Places API for restaurants
MEMORY_DB_PATH no Defaults to ./data/memory.sqlite
MEMORY_WINDOW_SIZE no Defaults to 20
AGENT_PERSONA no System prompt for agent personality
DEFAULT_LAT / DEFAULT_LON / DEFAULT_CITY no Default fallback location
OWNER_PHONE no Reserved for owner-level flows
ALLOWED_CONTACTS no Comma-separated allowlist
DEBUG no true enables verbose logs

Quick start

git clone https://github.com/Spkap/imessage-copilot.git
cd imessage-copilot
bun install
cp .env.example .env
bun run dev

You should see startup logs in this order:

Config validated
Initializing container
DB initialized
APIs healthy enough for startup
Watcher active
✅ iMessage Copilot is live

Requirements:

  • macOS with Messages.app signed in.
  • Full Disk Access configured so Photon can read Messages data.
  • Valid API keys if you want live LLM, weather, and places behavior.

Validation

bun run typecheck
bun run lint
bun run test
bun run validate

Current test status for this branch: 97 passing unit tests.

Engineering guardrails

  • No any in source.
  • Branded primitives for domain values such as phone numbers and coordinates.
  • Exhaustive discriminated-union handling via assertNever.
  • Zod startup validation for environment configuration.
  • Structured logging with correlation-friendly metadata.
  • Deduplication and batching to avoid double-sends and response spam.
  • Graceful shutdown on SIGINT and SIGTERM.

Product expectations

This build is tuned around one simple bar: the reply should feel like something you would actually want to receive in Messages. That means concise language, decisive recommendations, human confirmations, and fallback behavior that degrades safely instead of becoming robotic.

Documentation

Links

Attribution

Built on Photon’s iMessage SDK work:

  • @photon-ai/imessage-kit
  • @photon-ai/advanced-imessage-kit

About

Built on Photon's iMessage stack with TypeScript, Bun, and SQLite. Features intelligent intent routing, typed domain logic, Result-based error handling, and production-grade resilience patterns (retries, circuit breakers, batching).

Resources

License

Contributing

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors