Skip to content

janderswag/proposal-agent

Repository files navigation

proposal-agent

Turn a recorded discovery call into a branded, sendable proposal in ~4 minutes.

An open-source AI workflow that connects to your meeting recorder, extracts the deal from the transcript, and generates a fully-branded Quote + Statement of Work as PDFs — ready for e-signature.

Meeting webhook  ─►  extract (Haiku 4.5)  ──► pricing-readiness gate
   (Read.ai /            │                          │
    Fathom /             ▼                          ▼ (not ready: stop)
    Fireflies)    generate (Sonnet 4.6)       confirmation tool
                         │
                         ▼
                  critique (Sonnet 4.6) ──► passes? proceed : revise once
                         │
                         ▼
                  render PDF (React-PDF)  ──► Supabase Storage (signed URL)
                         │
                         ▼
                /d/[id] dashboard ──► View Proposal · Send · Delete · Edit name
                         │
                         ▼
                Send-for-signature (CLI today; in-product v2)
                  DocuSign · PandaDoc · SignWell · BoldSign · OpenSign · Wet Sign

Cost per proposal: ~$0.30–0.50 in LLM spend. ~10–12 page branded output. Inngest-orchestrated, durable, retriable.


Why this project is interesting (if you're reading on GitHub)

  1. Real production pipeline. Webhook signature verification, idempotent ingestion (unique partial index on (source, external_id)), atomic draft writes via Postgres RPC, signed-URL serving for private PDFs. Not a toy.
  2. Schemas as the contract. Every LLM call validates against a Zod schema. Failed validation triggers a retry-with-temperature-nudge before failing the row. No "hope the JSON parses" coding.
  3. Eval-driven prompt iteration. 5 scorers (schema validation, required fields, length discipline, pricing consistency, LLM-judge) run against a golden set. CI eval-regression gate fails any merge that drops avg score > 2%.
  4. Operator-tunable templates. Each vertical has a TypeScript template module, but pricing tiers, services catalog, ROI anchors, branding, and terms clauses all live in an editable /settings page that feeds the LLM prompt at runtime. No code commit needed to tweak your Pilot price.
  5. 6 e-signature providers + 3 meeting recorders + multi-tenant-ready schema (single-tenant in v1; tenant_id column on every table; RLS placeholder policies).
  6. Adversarial-review workflow. Every plan goes through /plan-eng-review: architecture/code-quality/tests/performance sections + outside-voice cross-model challenge via Claude subagent + Codex (when available). Past review reports live in docs/.

What's in the box

Pages

Path Purpose
/ Discovery calls list. Status filter chips, inline client-name edit, View Proposal / Send / Delete per row.
/d/[id] Per-call detail. Status-aware: queued / processing → live polling stepper; ready → PDF download + (v1.1) inline edit composer.
/d/new Manual paste — pipe any transcript through the same Inngest function the webhook uses.
/connections Meeting recorders (Read.ai / Fathom / Fireflies) + e-sign providers (DocuSign / PandaDoc / SignWell / BoldSign / OpenSign / Wet Sign).
/connections/{provider} Per-provider detail with env-var pills, "Test connection" or "Send test event" button, signed-cookie session, in-process rate limit.
/settings 7-section operator config: Brand, Operator, Pricing, Services, Scope, Terms, ROI. File upload for logo + signature image. Editable list of pricing tiers (1–4) with services catalog cross-referencing them.

Meeting-recorder ingestion (all live)

  • Read.ai — webhook + signed transcript ingestion
  • Fathom — webhook + signed transcript ingestion
  • Fireflies — webhook + signed transcript (inline-transcript toggle required; GraphQL fetch is v1.1)

E-signature providers (status-surface in v1; full send integration is v2)

Provider Cost (approx) Auth Notable
DocuSign $25+/mo JWT-grant access token Market leader, enterprise features
PandaDoc $19+/mo API key Document workspace + templates
SignWell $8/mo API key Cheapest hosted option
BoldSign $10/mo API key (US + EU regions) Cost-effective with region choice
OpenSign Free self-hosted Parse-style (App Id + REST Key) Open source; cloud option ~$10/mo
Wet Sign $0 none Signed-locally PDF, operator emails manually. Always-available fallback.

selectProvider() resolves the active provider in env-pinned → DocuSign → PandaDoc → … → Wet Sign order.

Architecture

  • Next.js 16 App Router on Vercel (Fluid Compute)
  • Inngest for durable workflow orchestration (extract → gate → generate → critique → revise → render → upload)
  • Supabase Postgres with RLS-ready schema; service-role admin client server-side
  • Supabase Storage for PDFs (quotes-sows, private + signed-URL serving) and brand assets (brand-assets, public for logo/signature URLs in rendered PDFs)
  • Vercel Blob for ephemeral pipeline-state polling JSON
  • Vercel AI SDK + @ai-sdk/anthropic for LLM calls; generateObject with Zod schemas for structured output
  • Langfuse for LLM observability (lazy-singleton with zero-overhead passthrough when env keys absent)
  • React-PDF for proposal PDFs
  • Zod for every schema boundary
  • Vitest for tests (currently 421 passing)

Data model (Postgres)

transcripts ──┐
              ├── 1:N drafts (current proposal lives here; is_latest=true)
              └── 1:N transcript_events (audit log; ON DELETE CASCADE)

operator_settings ── 1 row per tenant_id (JSONB; 7-section operator config)

Migrations in supabase/migrations/0001…0004_*.sql are declarative and idempotent.


Quickstart

1. Clone + install

git clone https://github.com/janderswag/proposal-agent.git
cd proposal-agent
npm install

2. Provision the cloud bits

You'll need accounts for:

  • Anthropic (Claude API)
  • Supabase (Postgres + Storage)
  • Vercel (hosting + Blob for state-store) — optional for CLI mode
  • Inngest (function orchestration) — optional for CLI mode
cp .env.local.example .env.local
# Fill in:
#   ANTHROPIC_API_KEY=sk-ant-...
#   NEXT_PUBLIC_SUPABASE_URL=https://<your-project>.supabase.co
#   SUPABASE_SERVICE_ROLE_KEY=...
# Apply migrations (Supabase SQL editor or migration runner)
# Provision Storage:
npm run supabase:storage:setup

3. Run a proposal — CLI

Save a meeting transcript to transcript.txt (any plain-text format). Then:

npm run prepare-proposal -- /path/to/transcript.txt

You'll get a PDF + an email draft in outputs/. The pipeline auto-opens the PDF in macOS Preview.

4. Run the dashboard

npm run dev          # Next.js dev server on :3000
npm run inngest:dev  # Inngest dev server on :8288 (separate terminal)

Then:

  • Open http://localhost:3000 — the discovery-calls list (empty initially)
  • Open http://localhost:3000/d/new — paste a transcript, click Generate, watch the pipeline
  • Open http://localhost:3000/settings — tune your branding + pricing tiers + services catalog
  • Open http://localhost:3000/connections — wire up Read.ai / Fathom / Fireflies webhooks + e-sign providers

5. Deploy

Push to a Vercel-connected repo:

  • Vercel auto-detects Next.js
  • Add ANTHROPIC_API_KEY, NEXT_PUBLIC_SUPABASE_URL, SUPABASE_SERVICE_ROLE_KEY, INNGEST_EVENT_KEY, INNGEST_SIGNING_KEY, CONNECTION_TEST_SECRET to Vercel env
  • Connect Inngest to your Vercel deployment (Inngest cloud → New App → enter /api/inngest URL)
  • Click Send test event on /connections/read-ai to validate the wire-up

Repo tour

app/
  api/
    connections/{provider}/test/   POST: test-connection routes (signed cookie + rate limit)
    transcripts/[id]/              GET/PUT/DELETE: row CRUD
    settings/                      GET/PUT: operator settings persistence + file upload
    webhooks/{read-ai,fathom,fireflies}/  Inbound webhook routes (HMAC-verified)
    inngest/                       Inngest serve endpoint
    proposal-pdf/[…]/              Signed-URL minting for private PDFs
    generate/                      Manual-paste pipeline entry
  components/dashboard/            Header, TranscriptRow, modals, overflow menu
  connections/                     Server-rendered Connections pages
  d/[id]/                          Per-call detail surface
  d/new/                           Manual-paste UI
  settings/                        Editable Settings page + ImageUploadField

lib/
  schemas/        Zod: MeetingExtract, ProposalDraft, CritiqueReport, OperatorSettings
  prompts/        Versioned system + user prompts (extract, generate, critique)
  pipelines/      Pipeline step functions + Inngest orchestrator + CLI runner
  templates/      Per-vertical TemplateModule factories (ai_automation, professional_services)
                  Settings-driven prompt composition via compose.ts
  render/         React-PDF components + design tokens
  esign/          Provider-agnostic envelope-send interface
  ingest/         Per-provider adapters (Read.ai, Fathom, Fireflies)
  dashboard/      Transcript repo, state-store, queries, settings repo
  observability/  Langfuse wrapper (lazy singleton)
  notify/         Slack webhook + email draft builder
  eval/           Runner + scorers + golden set

docs/
  design.md                                 v1 design doc
  settings-v1-plan-2026-05-25.md           Settings page plan (post /plan-eng-review)
  connections-v1-plan-2026-05-25.md        Connections page plan
  coding-guidelines.md                     House rules

supabase/migrations/
  0001_initial_schema.sql                  transcripts + drafts + events
  0002_webhook_idempotency.sql             unique partial index on (source, external_id)
  0003_create_draft_atomic.sql             atomic demote-then-insert RPC
  0004_operator_settings.sql               operator_settings JSONB table

tests/evals/                               Golden-set (transcript, ideal_proposal) pairs
.github/workflows/eval-gate.yml            CI gate — fails merge on > 2% eval-score drop

Commands

Command What it does
npm run prepare-proposal -- <transcript.txt> CLI pipeline end-to-end
npm run dev Next.js dev server
npm run inngest:dev Inngest dev server
npm run typecheck tsc --noEmit
npm test Vitest (~421 tests, ~1s)
npm run lint ESLint
npm run eval Eval suite against tests/evals/ (real Sonnet calls)
npm run eval:gate Eval + baseline-regression gate (used in CI)
npm run eval:baseline Capture current eval scores as the new baseline
npm run calibrate-threshold One-off script: critique-score distribution sweep for the v1.1 needs-review threshold
npm run supabase:storage:setup Provision the quotes-sows + brand-assets Storage buckets
npm run send-for-signature -- <draftId> CLI: send a rendered proposal via the configured e-sign provider

Design principles (and why this project might be worth your time)

  1. Schemas first. Never write LLM-calling code before the Zod schema exists. The schema is the contract — generate, validate, retry, repair.
  2. Eval before prompt. Never ship a prompt change without running the eval suite. CI fails on > 2% regression. The threshold is in tests/evals/baseline-scores.json.
  3. Single source of truth for pricing. Pricing lives in ProposalDraft.investment.tiers — every section that references prices pulls from there at render time. No hardcoded prices in template prose.
  4. Templates are data. Per-vertical structural prose lives in lib/templates/{vertical}.ts. Operator-tunable values (tiers, roles, anchors, terms) live in operator_settings. Templates compose the two at prompt-build time via lib/templates/compose.ts.
  5. Prose only in slots. The LLM writes inside section slots. Section ordering, structure, and presence are deterministic code.
  6. Treat transcripts as untrusted. Webhook payloads wrapped in <transcript> delimiters. Transcript content never flows into tool-use decisions.
  7. Human in the loop, always. No proposal sends without operator approval. Send-for-signature is a deliberate operator action.
  8. No browser storage in artifacts. No localStorage/sessionStorage anywhere in the pipeline.
  9. Boil the lake when AI makes it cheap. Test coverage, edge cases, error paths — the marginal cost is near-zero, so do the complete thing.

Status

Metric Value
Commits on main 55+
Tests 421 passing
LLM cost per proposal ~$0.30–0.50
End-to-end latency (P95) < 5 minutes
Meeting-recorder integrations 3 (Read.ai, Fathom, Fireflies)
E-sign provider surfaces 6 (DocuSign, PandaDoc, SignWell, BoldSign, OpenSign, Wet Sign)
Postgres migrations 4 (initial + idempotency + atomic-RPC + operator-settings)
Eval golden set 15 cases (10 synthetic + 5 anonymized real)
Vertical templates 2 registered (ai_automation, professional_services); home_services + b2b_agency enum-only

What's deliberately out of scope (v1)

  • Auto-send proposals. Human approval required, always.
  • Dashboard-initiated Send-for-signature. CLI works today; in-product button is v2.
  • Payment collection. v3+.
  • Multi-tenancy. Single-tenant v1; tenant_id columns + RLS placeholders ready for v2 Clerk integration.
  • Mobile UI. Web only.
  • Client-facing portals / CRM features. Not the product.
  • RAG over past proposals. v3.

If a feature isn't on this list, it's not in v1. Don't argue with the doc; argue with the doc before writing code.


Contributing

This project ships under the philosophy that every plan goes through /plan-eng-review and every PR goes through /review — both are skills in this repo's gstack toolset. The plan-review workflow surfaces architecture-level catches before code lands; the PR-review workflow runs adversarial passes (Claude subagent + Codex when available) on the diff.

If you want to contribute:

  1. Open an issue describing the problem
  2. Wait for a design discussion (or write a design doc yourself)
  3. Run /plan-eng-review against the design before implementing
  4. Run /review against the PR before opening

See docs/ for past plan + review artifacts.


License

MIT. Use it, fork it, embed it in your own AI agency tooling. PRs welcome.


Built with care by @janderswag for Hawkify, an AI implementation agency. The dogfood scenario: every Hawkify discovery call produces a draft proposal automatically. The open-source goal: any operator running a service business should be able to spin this up against their own Anthropic / Supabase / Inngest stack and have the same workflow.

About

Turn discovery call transcripts into branded, vertical-specific proposals and SOWs (Claude pipeline: extract → generate → critique → render).

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages