This is the v1 starter. Spine is shipped — everything below is yours to extend.
Every sponsor lives in lib/integrations/<vendor>/:
agentphone/
├── index.ts # Exports the public typed client — DO NOT modify the interface
├── mock.ts # Deterministic stub for demos. ALWAYS WORKS, no env required.
└── live.ts # Real API calls. Reads keys via lib/env.ts.
index.ts picks mock vs live via two env vars:
- Global default:
INTEGRATION_MODE=mock|live - Per-vendor override:
GEMINI_MODE=live,AGENTPHONE_MODE=live, etc.
This means you can flip a single integration to live (e.g. Gemini, once the API key is in your .env.local) while keeping the rest mocked. Demo never breaks.
- Drop the key in
.env.local(use.env.exampleas the template). - Open
lib/integrations/<vendor>/live.tsand implement the methods. The signature comes from the interface inindex.ts— TypeScript will tell you what's missing. - Flip the mode:
<VENDOR>_MODE=livein.env.local(orINTEGRATION_MODE=livefor everything). - Confirm:
pnpm test— contract tests intests/integrations/adapters.test.tscover shape, not behavior, so they should still pass for live mode if your impl honors the interface.
mkdir lib/integrations/<new-vendor>/and addindex.ts,mock.ts,live.ts.- Define the public interface in
index.ts. Follow the existing pattern — seelib/integrations/gemini/index.ts. - Use
pickImpl()fromlib/integrations/adapter.tsto swap mock/live. - Add a contract test to
tests/integrations/adapters.test.ts. - If the vendor needs env vars, add them to the Zod schema in
lib/env.tsand to.env.example.
lib/store/memory.ts is the v1 in-memory map. The shape is intentionally minimal so you can swap implementations without touching the rest of the app.
Recommended v2 path:
- Replace each
Mapwith calls to Supermemory's recall/remember API (already wired inlib/integrations/supermemory/). - Keep the same method signatures (
upsertJob,listJobs,appendEvent, …) so route handlers and components are untouched. - Or: use Supabase / Postgres for structured rows + Supermemory for unstructured context recall. Both work.
- Monochrome only. No color in the UI beyond black, white, and the
zincscale. Status is conveyed by fill, ring, dash, and motion — never hue. - Lucide icons only. No emoji in UI strings.
- Server components by default. Use
"use client"only for actual interactivity (polling, dialogs, click handlers, sidebar active state). - Zod at boundaries. Validate every route handler input. Validate every live-mode external response.
- No
any. Useunknown+ type guards when shape is dynamic. paramsis a Promise in Next 16. Destructure withconst { id } = await params.- Every job state change appends a
JobEvent. That's what feeds the timeline component.
- Open
/— landing renders cleanly. - Open
/dashboard— seeded job is visible. - Trigger a fresh inbound call via the curl in the README. The new job appears within the 5s poll interval.
- Click into the job — timeline shows the full orchestration sequence.
- (Optional)
/dashboard/settingsshows which integrations are wired live vs mock.
- Property/owner auth. v1 has no auth. Add Clerk or NextAuth for the dashboard before any external pilot.
- Tenant verification. AgentPhone currently trusts the caller ID. Real product needs property lookup + secondary verification (unit number, etc.).
- Contractor onboarding. v1 has a static seed pool + Browser Use discovery. Real product needs a contractor-facing flow.
- Payment splits. Sponge wires owner → contractor. Whether to take a platform fee, and how, is a product call.
docs/superpowers/plans/2026-05-17-call-my-agent-v1.md
Every file in this repo traces back to a section of that document. If you're adding something new, consider drafting a follow-up plan in the same directory so the next person to touch the area has a map.