Show full chat transcript on leads, fix Chat Agent settings losing place on Drive connect
Seth+1: Claude Sonnet 5
41f3768
Chat-agent leads only showed their latest message in the lead detail
panel ("Their reply"), truncating the actual conversation. Added a
chat-agent-thread endpoint (reads the transcript already stored on
raw_webhook.messages) and load it automatically for these leads instead
of requiring a click that previously hit the Smartlead-specific thread
route and failed.
The Chat Agent settings page kicked back to the main Settings list after
connecting Google Drive. The OAuth callback always redirects to the bare
/settings URL, but which settings sub-panel was open only lived in local
component state, not the URL — so landing back on /settings forgot Chat
Agent was open. Settings.jsx now checks for the Drive-callback query
params on load and reopens straight into the Chat Agent panel.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Fix chat agent web search, greeting resume, and lead capture gaps
Seth+1: Claude Sonnet 5
b5da8dd
Competitor comparison tables defaulted straight to "Requires confirmation"
because the agentic prompt never told the model to actually use its
search_the_web tool for the comparison — only that the tool existed.
Now the model is instructed to search before falling back, and to label
web-sourced cells as unverified public info rather than a settled claim.
The widget's opening greeting silently disappeared on any repeat open
within the same browser (existing localStorage session skipped the
greeting fetch entirely, with no history shown either). Added a session-
resume endpoint so reopening replays prior messages, or shows the
greeting if nothing was exchanged yet.
Chat-agent-sourced leads were linked into Contacts the same way as
Smartlead reply-imports, which the enrichment "Fetch" gate explicitly
excludes — and no extraction logic existed at all for company name, only
visitor name. Chat-agent leads now get form-style (enrichable) contacts,
and company name is extracted from the conv…
Skip email generation for no-email contacts, make Stop generation more robust
Seth+1: Claude Sonnet 5
2d73657
Contacts with no email address (LinkedIn-only prospects) were being run
through generate_email_sequence anyway, which always raised and got
reported as a "failed" contact needing retry — even though retrying can
never succeed without an email on file. Now skipped cleanly with no
error, leaving LinkedIn generation for them unaffected.
Stop generation's cancel request had no failure handling, so a dropped
or errored POST left the button stuck on "Stopping…" with generation
still running server-side. Added a retry + surfaced error, plus a
"Finishing current contact…" label after a few seconds so a genuinely
in-progress stop (bounded by the current contact's in-flight GPT call)
doesn't read as frozen.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
LinkedIn connects/DMs are now throttled per connected account against
conservative daily/weekly caps (rolling windows, randomized jitter) enforced
by a new background scheduler so delivery keeps progressing without a
browser tab open. Email sending gets per-campaign timezone/business-hours/
interval controls set during campaign setup. Content generation can now be
stopped mid-run and resumed later without restarting.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Add dashboard activity/lead/deal stat boxes and backfill Contacts/Companies ownership scoping
Seth+1: Claude Sonnet 5
de29580
- Add Calls, Visits, New leads, Deals, and Closings stat boxes to the
Sales Dashboard, respecting the existing team/date-range filters.
- Add a tenant-wide GET /api/activities endpoint (previously only
available per-contact) and bump /api/leads' page-size cap to match
Deals, so the new dashboard counts aren't silently truncated.
- Give Contact and Company an owner_user_id column and apply the same
team-manager visibility scoping already used by Leads/Deals to their
list, detail, and patch routes — these had no ownership concept at
all before, so every tenant member could see everyone's contacts and
companies regardless of team.
- Add an Owner avatar + reassignment dropdown to Contacts.jsx and
Companies.jsx (factored into a shared OwnerCell component), matching
the admin-can-assign-anyone / member-can-only-self-claim rules the
backend now enforces.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Allow LinkedIn generation for contacts with no email but a LinkedIn URL
Seth+1: Claude Sonnet 5
56eda2f
Apollo CSV exports frequently can't verify an email but still include a
valid LinkedIn profile URL — generate_linkedin_sequence() was hard-
requiring an email and failing these contacts entirely, even though
LinkedIn sending never uses email (it already resolves the profile from
the LinkedIn URL at send time). Also fixes contact field extraction
(both email and LinkedIn generation) to recognize Apollo's actual
column names like "Company Name" instead of only "Company", which was
silently leaving company blank for every Apollo-sourced contact.
- gpt_service.py: case-insensitive field lookup with the same alias
list main.py already uses elsewhere; LinkedIn generation now only
requires a name, email, or LinkedIn URL — not specifically an email.
Email generation still requires an email (needed to actually send).
- Give generate_email_sequence the same typed generation_failed marker
treatment as LinkedIn instead of silently returning fake fallback
email content on error.
- main.p…
Fix LinkedIn campaign generation reliability, quality, and persistence
Seth+1: Claude Sonnet 5
810c33d
- Stop silently swallowing generation errors as fake generic fallback
content; retry once on transient OpenAI failures, then surface a
typed failure instead of writing fabricated sequence rows.
- Fix step-order/message-numbering bugs so LinkedIn steps keep the
wizard's configured order and messages aren't mislabeled by list
position.
- Enforce the LinkedIn connection-note character cap server-side and
tighten the prompt to require concrete personalization and ban
generic filler openers.
- Give campaigns a real backend record (UploadedFile campaign_name/
status/updated_at + list/detail/meta-patch/delete routes) instead of
browser localStorage, which had a bug where a parse failure would
silently repopulate demo data over real campaigns.
- Reconcile the dashboard from the backend list, auto-save campaign
identity from the wizard on every step, and re-fetch the saved
sequence plan/prompts when reopening a campaign so content and
configuration survive closing and re…
Add sales teams: manager role, team assignment, and team-scoped data visibility
Seth+1: Claude Sonnet 5
67330bb
Managers head a team (Team.manager_user_id, TenantMembership.team_id) and see
their own plus their team's leads, deals, and Call/Visit activity; other
members see only their own. A shared _visible_owner_user_ids() helper gates
list_leads, list_deals, quarterly_recurring_metrics (which the Dashboard
reads from), and both activity-timeline endpoints. Leads gains an
owner_user_id column plus assignment UI (mirroring Deals, which already had
per-user ownership) since team-scoping needs something to scope by. Admins
manage teams and member-team assignment from a new Teams card in Settings.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Add Call/Visit activity logging and timeline for contacts and companies
Seth+1: Claude Sonnet 5
41c19e3
New ContactActivity table tracks calls/visits with notes and author,
surfaced via Call/Visit icon buttons (with an inline note textarea)
everywhere a contact appears — Contacts table rows, associated-contacts
lists on Companies/Leads/Deals, and the contact identity header in each
detail panel. Adds a per-contact activity timeline and a company-level
rollup that aggregates activity across every contact sharing that company.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Add Type field (Existing/New) to Contacts/Companies/Leads/Deals, synced like Category
Seth+1: Claude Sonnet 5
fe7e466
Company gets a new type column, wired through the existing generic
COMPANY_DETAIL_PATCH_FIELDS propagation so it syncs bidirectionally across
Company/Contact/Lead/Deal exactly like Category. Companies page also gets a
Type filter, table column, and inline-add-row selector matching Category's
existing footprint there.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Add contact number field to Contacts/Companies, show associated contacts on Leads/Deals
Seth+1: Claude Sonnet 5
c40de6e
Adds a personal phone column on Contact/CrmLead and a synced company-level
phone on Company (using the existing company_phone/company_name_for_emails
style patch-body disambiguation), surfaced in the shared identity/company
editors everywhere they're used. Leads and Deals detail panels now also show
an "Associated contacts" list for the linked company, matching the Companies
page, via a new shared AssociatedContactsList component and a
GET /api/contacts/by-company endpoint.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Fix Category revert bug on Contacts page, generalize sync to all company fields
Seth+1: Claude Sonnet 5
4b1d42b
- Contacts.jsx built its own local companyDetails object for the
editor instead of passing raw_data straight through — that object
was missing a Category key entirely, so the field always read as
empty and every autosave round-trip re-synced the form from that
same Category-less object, snapping the dropdown back to "-".
- Generalized the previous Category-only propagation into full-field
propagation: editing industry, employees, revenue, website, city,
state, country, or category anywhere (Contact/Lead/Company/Deal)
now pushes to the Company row and every Contact/Lead sharing that
company name, not just Category.
- _sync_companies_from_contacts now also tops up any blank field on
an already-existing Company row from the richest matching contact,
fixing Company rows that got stuck sparse after an early sync —
never overwrites a field that already has a value, synced or
manually edited.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Sync Category across Company/Contact/Lead, link contacts, add company-level lead conversion
Seth+1: Claude Sonnet 5
6a71a79
- Category is now a true company-level attribute: editing it from the
Company page, a Contact, a Lead, or a Deal linked to a contact
propagates to the Company row and every Contact/Lead sharing that
company name (case-insensitive), so it never drifts between the
four places it's editable.
- Fixed a gap where converting a contact to a lead didn't carry its
Category into the lead's company snapshot.
- Associated contacts in the Company detail panel are now clickable —
navigates to Contacts and opens that exact contact's detail panel
(via a short-lived ?contact= deep link, cleaned from the URL after
use).
- New "Convert to Leads" action on the Company detail panel, mirroring
the existing per-contact "Copy to Leads," but scoped to every
contact associated with that company in one action.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Add Companies page with Category field and contact sync
Seth+1: Claude Sonnet 5
c253255
- New Company entity (name, category A/B/C, industry, employees,
annual revenue, website, city/state/country) with routes to list,
create, patch, bulk-delete, and re-sync.
- Companies are associated with Contacts by case-insensitive name
match rather than a foreign key, so this is fully additive and
never touches the existing Contact/CrmLead/CrmDeal schema or
behavior. A Company row is auto-created for any distinct
Contact.company value that doesn't already have one, seeded from
the richest matching contact; manual edits are never overwritten
by later syncs.
- New Companies page (sidebar, between Contacts and Leads) built
from the same AppShell/MainTableWorkspace/SlideOverPanel
components as Leads, with a detail panel listing every associated
contact plus an inline "add a contact" form scoped to that company.
- Category dropdown (A/B/C) added to the shared CompanyDetailsEditor
used by Contacts, Leads, and Deals, so it's consistently available
wherever company …
Add Chat Agent module: RAG website widget with Drive/site knowledge base and CRM lead capture
Seth+1: Claude Sonnet 5
991c079
Adds an embeddable WordPress chat widget backed by a Google Drive folder and a
site crawl, with admin-editable skills/guardrails, streaming replies, and
automatic CrmLead creation (with live transcript) for every chat that shares
a name/email.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>