Multi-domain agentic platform. Three pillars: B2B lead generation, job-application automation, and product/sales-tech intelligence. Next.js 16 frontend + GraphQL API backed by Cloudflare D1 (SQLite), with AI/ML pipelines for company enrichment, contact discovery, and outreach automation. (The former autonomous research-paper authoring & publishing pipeline was retired from the Python backend; the canonical runner now lives in the Rust crates/research-pipeline/.)
A Python LangGraph backend (backend/) powers every agent workflow — email composition, reply, outreach, durable multi-touch campaigns, enrichment, classification, RAG, and text-to-SQL (~50 registered graphs; see backend/infra/registry.py) — built on LangChain primitives (ChatOpenAI against DeepSeek through the Cloudflare AI Gateway) and traced end-to-end through LangSmith.
Next.js (Vercel) -> Cloudflare D1 (SQLite) via Drizzle sqlite-proxy over the D1 REST API (src/db/d1-proxy.ts).
Discovery Common Crawl / live fetch -> companies -> D1
Enrichment Company IDs -> LangGraph (company_enrichment) -> D1
Contacts LinkedIn / manual -> LangGraph (contact_discovery) -> D1
Outreach Contacts -> LangGraph (email_outreach / campaign) -> CF Email Sending worker -> D1
Serving Browser -> Apollo Client -> /api/graphql -> Drizzle ORM -> D1
Evaluation run_evals.py --gate (LLM-as-judge, aggregate >= 0.80)
backend/ holds the LangGraph StateGraphs (email_compose, email_reply, email_outreach, email_orchestrator, campaign, text_to_sql, company_enrichment, contact_discovery, contact_enrich, the classify_* family, agentic_rag, pipeline, …). Next.js calls them through runGraph() / runGraphWithMeta() / streamGraph() in src/lib/langgraph/index.ts, which uses the @langchain/langgraph-sdk Client.runs.wait → POST ${LANGGRAPH_URL}/runs/wait.
Two runtimes share the same graph code:
| Mode | Runtime | Checkpointer |
|---|---|---|
| Local dev | langgraph dev on :8002 |
in-memory |
| Production | FastAPI + uvicorn on Render (agentic-sales.onrender.com, render.yaml) |
Cloudflare D1 via AsyncCloudflareD1Saver |
Render's free tier idles, so the first /runs/wait after inactivity cold-starts (~30–60s).
LangSmith tracing turns on with LANGSMITH_TRACING=true + LANGSMITH_API_KEY and is a strict no-op when off. JS-side wrapping uses wrapOpenAI from langsmith/wrappers in src/lib/ai-gateway/client.ts so DeepSeek calls from the Next.js side land in the same project.
Company profiles, contacts management, multi-touch email campaigns, and streaming
composition. All outbound mail funnels through a single Cloudflare Email Sending
worker (workers/email/); the TS adapter is src/lib/email/email-adapter.ts. Sent,
received, and scheduled mail share one unified emails table (direction column).
Multi-touch outreach runs as a reactive, durable LangGraph thread — one per
(campaign, contact), checkpointed in D1 (backend/graphs/campaign_graph.py). It is
draft-first: every touch is generated and persisted for human approval before
anything sends. Cadence is cron-driven by workers/campaign-runner/ (*/5), and the
sequence stops automatically on an inbound reply.
Apollo Server 5 with typed resolvers, DataLoaders, full codegen pipeline, and custom scalars.
Email graphs (email_compose, email_reply, email_outreach, email_orchestrator) drive composition and follow-ups; the campaign graph runs durable multi-touch sequences; enrichment graphs (company_enrichment, contact_discovery, contact_enrich) extend CRM records from web + LLM signals; the agentic_rag retrieval graph (full chain + a fast mode="retrieve" path) handles long-horizon investigation; text_to_sql powers natural-language queries. Runs are checkpointed in D1 so they resume cleanly across container restarts.
Lead discovery is organised around 8 applied-AI verticals (legal-PI, immigration, health, voice-ops, fintech, customer-support, construction-estimating, accounting) — each with rotating sub-niches, calibrated scoring, ATS job-board targets, and tailored outreach sequences. See docs/micro-verticals/README.md.
Every LLM and tool call is traceable in LangSmith when the env vars are set. The eval gate (pnpm test:eval → cd backend && uv run python scripts/run_evals.py --gate) scores graph changes with LLM-as-judge and deterministic checks, holding an aggregate pass rate of ≥ 0.80. LangSmith analytics surface in the UI through the langsmith Apollo resolver (src/apollo/resolvers/langsmith.ts).
# Dev & build
pnpm dev # Next.js dev server (port 3004)
pnpm backend-dev # langgraph dev on port 8002 (LangGraph Studio)
pnpm build # Production build
pnpm lint # ESLint
pnpm codegen # GraphQL codegen (run after schema changes)
# Database
pnpm db:generate # Generate Drizzle migrations
pnpm db:migrate # Apply migrations
pnpm db:studio # Drizzle Studio
# Lead generation pipeline (Python LangGraph CLI)
make start # Full cycle: discover -> enrich -> contacts + qa -> outreach
make start-status # Pipeline status and phase detection
make start-top N=50 # Show top leads
# Evals (run after any LLM/prompt change — gates at >= 0.80)
pnpm test:eval # cd backend && uv run python scripts/run_evals.py --gate
# Skills
pnpm skills:seed # Seed skill taxonomy
# Deploy
pnpm run deploy # Vercel deploy (NOT `pnpm deploy` — that hits pnpm's builtin)schema/ GraphQL schema (by domain: companies, contacts, emails, ...)
migrations/ Drizzle migration SQL files (Cloudflare D1)
scripts/ TypeScript automation
docs/ Architecture and research documentation
workers/ Cloudflare Workers (email, campaign-runner, ai-gateway, contacts, store-gateway)
backend/ Flattened LangGraph backend (no `agentic_sales/` wrapper)
graphs/ LangGraph StateGraphs (email, campaign, enrichment, RAG, text-to-sql, classifiers)
infra/ registry.py (GRAPHS list), db hub, custom_app, _cron, otel/langsmith setup
llm/ client.py (make_llm — ChatOpenAI factory for DeepSeek), prompt_safety, struct_output_registry
domain/ schemas/ clients/ memory/ utils/ eval/ redteam/ supporting packages
tests/ pytest gates + golden datasets
scripts/ run_evals.py (gate) + pipeline CLIs (leadgen_metal_cli.py, …)
src/
__generated__/ Codegen output (types, hooks, resolvers)
apollo/ Apollo Server setup, resolvers, DataLoaders
app/ Next.js App Router
(pages)/ companies, contacts, follow-ups, admin, settings, auth
api/ graphql, emails, companies, rag, gh, opportunities, linkedin, webhooks, admin
components/ React components
db/ Drizzle schema + Cloudflare D1 client
graphql/ Query/mutation/fragment documents
lib/
langgraph/index.ts runGraph / runGraphWithMeta / streamGraph → POST ${LANGGRAPH_URL}/runs/wait
ai-gateway/client.ts LangSmith-wrapped DeepSeek client (Cloudflare AI Gateway)
email/ CF Email Sending adapter, campaign scheduler, cadence, reply generation
skills/ Skill taxonomy, extraction, filtering
auth/ Client + server auth
ml/ Opportunity classifier + ML helpers
recipes/ constants/ hooks/ schema/ supporting modules
tools/database/ Agent database tools
| Route | Purpose |
|---|---|
/api/graphql |
Apollo Server GraphQL endpoint |
/api/auth/[...path] |
Better Auth |
/api/emails/send |
Send email |
/api/emails/generate-stream |
Streaming email composition (SSE) |
/api/emails/orchestrate |
Orchestrated compose + send |
/api/emails/schedule-stream |
Scheduled-send streaming |
/api/companies/enhance |
AI company enhancement |
/api/rag/stream |
RAG streaming |
/api/gh/quick-brief |
GitHub lead quick brief |
/api/opportunities/eval |
Opportunity eval |
/api/linkedin/* |
LinkedIn scrape endpoints |
/api/webhooks/langsmith-alerts |
LangSmith alert webhook |
/api/admin/* · /api/csp-report |
Admin console · CSP reports |
MIT