Commit Graph

314 Commits

Author SHA1 Message Date
Matthew Meszaros 8f465fdb1c feat: give each mailbox a human sending persona (randomized daily and hourly caps, send spacing, work start/end, lunch break and working weekdays, rolled once per local day in the mailbox's own timezone and applied across the campaign, warmup and smart-send schedulers), add campaign auto-pause guardrails that stop a campaign when its bounce, complaint or reply rate leaves the configured band, make mailbox rotation actually rotate for tag-resolved and all-mailbox campaigns, stop every scheduler from ever returning a slot in the past, and correct the mailbox min-gap field that stored seconds while labelling them minutes 2026-08-13 16:51:29 +02:00
Matthew Meszaros 8bd2c2b57a feat: make self-hosting work end to end and rewrite the guide around what was tested (#97) 2026-08-13 09:47:46 +02:00
Matthew Meszaros 5e6287c920 feat: add the Advisor, continuous sending checks surfaced on the row they are about (#86)
* feat: index advisor findings by subject and parent entity so a list page fetches its whole surface once and every row resolves its own advice from the shared cache instead of firing a request per row

* feat: rebuild the advisor fix drawer as a three-screen resolution flow (why it fired with the measured evidence, the exact before and after, then an animated outcome with undo) with a progress rail and direction-aware transitions, and deep-link manual fixes to the screen where they are made

* feat: add AdvisorRowFlag, the inline per-row advisor indicator that renders on the mailbox or campaign the problem is about and opens that row's findings in an anchored panel instead of making the reader join a card list against a table

* feat: add AdvisorSummaryBar, a one-line collapsible page summary that replaces the stack of advisor cards above a list, counts the distinct rows implicated rather than the findings, and forces itself open only for critical or workspace-level advice no row flag can carry

* feat: put advisor advice on the mailbox row it is about in the accounts list, replace the card stack above the table with the collapsible summary bar, and support ?mailbox=<id> so a finding can deep-link straight to the mailbox detail instead of the top of the list

* feat: flag advisor findings on the campaign row in the campaigns list, including step-level copy problems which index onto their parent campaign since a step has no row of its own, and add the collapsible summary bar above the list

* feat: move the deliverability and contacts pages onto the collapsible advisor summary bar so their findings stop pushing the numbers they describe below the fold

* feat: add an ordered Steps field to advisor findings, persisted as text[] and always refreshed from the current build, and write real how-to steps for the deliverability checks that have no one-click fix (bounce rate, spam placement, tracking domain, and per-record SPF/DKIM/DMARC instructions)

* feat: write ordered how-to steps for the manual advisor findings where the remedy alone leaves someone stuck (broken template syntax, missing first-name fallback, unsubscribed contacts still enrolled, a campaign with no resolvable sender, and a mailbox that lost warmup pool standing) and correct the personalization detail that named a merge syntax this product does not use

* feat: show a mailbox's advisor findings at the top of its detail drawer, which is where both the row flag and the ?mailbox deep link now land

* feat: open the resolution flow from findings that have no one-click fix too, since the ordered how-to lives there and a card with no Fix button previously left the steps unreachable

* docs: document the per-row advisor flags, the collapsible page summary, the three-screen resolution flow, and the ordered manual steps for findings with no one-click fix

* feat: align the advisor summary bar to the px-5 page gutter used by SectionBar and the list rows on all four surfaces, instead of sitting flush against the edge while the table it describes is indented

* fix: stop the resolution drawer collapsing to zero height between screens by switching the step transition to popLayout with a layout-animated container, so the dialog resizes into the next screen instead of snapping shut and reopening

* feat: wire the advisor repository, narrator, service, tool registration, and background runner into the backend boot path so findings evaluate on a schedule and the assistant can read them

* docs: register the advisor guide in the sidebar, add its endpoint scope table to the API reference, and document the sandbox advisor showcase

* fix: darken the advisor nav badge to solid orange-600 on white instead of a pale amber-100 chip that read as a disabled control beside the sidebar's saturated indicators, and drop the critical badge to rose-600 so the two stay in the same weight class

* fix: use orange-500 for the advisor nav badge, matching the high-severity dot on the row it points at, rather than the darker orange-600

* feat: add an Auto safety class to advisor actions and mark the seven fixes autopilot may apply unattended (the cap cuts, the send-gap widen, the campaign limit matches, and the unsubscribe header), with a test pinning the boundary so nothing that halts sending or generates new outbound mail can drift into it

* feat: add advisor autopilot, which applies the auto-safe fixes unattended as the member who switched it on, resolving their live permissions each run so it fails closed when they leave the org, bounded to 10 changes per evaluation and audited per fix like any hand-made change

* feat: add the advisor agent fix, a bounded per-finding agent run that resolves the problems a settings change cannot (broken template syntax, bulk-reading copy, shared-inbox lists) as the calling member inside a tool allowlist scoped to the finding's category, metered per iteration and marked applied only when it actually called a write tool

* feat: surface autopilot and the agent fix in the dashboard, adding the workspace toggle that names exactly which changes it may make, an Auto chip on the findings it is allowed to take, and an agent-fix path in the resolution drawer that reports the tools it actually called rather than only its own account of them

* docs: document the agent fix and autopilot, naming the exact set of changes autopilot may make, that it acts as the member who enabled it and stops when they leave, and why the agent-fix endpoint is JWT only

* fix: gate the agent fix per detector instead of per category, so a missing DMARC record no longer offers a Fix-with-agent button it can never satisfy and then reports failure; findings whose fix lives in DNS or a provider console now show their manual steps, and the client is told which is which via agent_fixable

* feat: soften the advisor surfaces to translucent washes, replacing the filled nav badge with a tinted pill that carries its colour in the text, frosting the row panel and the resolution drawer, and turning the severity chips and cards into layers the page shows through

* docs: correct the agent-fix scope to name the findings it cannot resolve, and why a DNS record shows steps instead of a button

* feat: ship the actual DNS records for the findings that live outside the platform, with the provider's SPF include resolved, the DMARC record scoped to the sending domain and starting at p=none, the DKIM host plus the console that generates its value, and a tracking CNAME pointing at this install's own tracking host

* feat: render advisor snippets as labelled copy-button rows so a DNS record is one click per field rather than a text-selection exercise, with no copy affordance on a value the server could not supply

* docs: document the pasteable DNS records and the guarantee that every check offers a fix, an agent, or ordered steps

* fix: bump golang.org/x/text to 0.39.0 to clear CVE-2026-56852, a HIGH-severity infinite loop in norm.Iter that Trivy started failing the security scan on
2026-07-30 17:15:09 +02:00
Matthew Meszaros 9a67008ea2 feat: add a runtime config shim to the dashboard so a single built image reads its API url, app url, tracking domain, and turnstile key from container env via /config.js 2026-07-22 18:40:06 +02:00
Matthew Meszaros 666ea44256 fix: type the conditional token build/parse helpers with a uid-less subset so the web typecheck passes on the transient uid attribute 2026-07-22 18:40:06 +02:00
Matthew Meszaros 470d6f43a8 feat: replace the Write with AI blob action with an inline caret AI assistant in the campaign email editor 2026-07-22 17:04:51 +02:00
Matthew Meszaros 1750d4eb45 feat: wire variable chips, per-recipient AI blocks, and conditionals into RichTextEditor, EmailContentEditor, the preview renderer, and chip styles 2026-07-22 17:04:51 +02:00
Matthew Meszaros 71df2c8a6d feat: add atomic TipTap variable, AI-variable, and conditional chip nodes with literal-token serialization for the campaign email editor 2026-07-22 17:04:51 +02:00
Matthew Meszaros bb040828b1 feat: rework automation AI steps to mirror campaign step types with one agentic AI step plus an AI switch, per-case switch handles on the canvas, a Stop node, and a step-type add menu 2026-07-22 10:58:35 +02:00
Matthew Meszaros f48ae60476 feat: build the ai step editors and smooth out the flow lines 2026-07-21 17:35:11 +02:00
Matthew Meszaros ffc219a66d feat: clean up the dither charts and give them more room 2026-07-21 17:35:11 +02:00
Matthew Meszaros f898e4975f fix: show the real plan badge, mailbox tags and live sidebar stats 2026-07-21 17:35:11 +02:00
Matthew Meszaros 8055c2f05f Merge remote-tracking branch 'origin/main' into self-host/local-stack
# Conflicts:
#	cmd/backend/main.go
2026-07-20 11:53:21 +02:00
Matthew Meszaros 248347c8e6 feat: warn on the mailboxes page when too few mailboxes are warming for a healthy pool, with an add-mailboxes action and a cloud option 2026-07-20 11:50:53 +02:00
Matthew Meszaros 0186ed5c78 feat: web notification center + digest settings: the bell dropdown becomes a real notification center with per-category icon chips and tones, Today/Yesterday/Earlier group headers, compact relative timestamps, unread accent styling, All/Unread filter pills, first-load skeleton, empty state and a settings footer link; the settings page gains Billing and Team category sections and an Email delivery section using the shared OptionSelect radio idiom (Instant/Smart with Recommended badge/Hourly/Daily) riding the same autosave PUT, with the read-in-app/security-immediate/shared-email note; the preferences model adds billing_alert, team_activity and the email_digest union, and OptionSelect labels widen to ReactNode for the badge 2026-07-20 07:21:20 +02:00
Matthew Meszaros 296e781ce5 feat: scoped Auto in the dashboard From menu: filtering by a mailbox tag turns the Auto row into Auto in <tag> previewing the best-scored member, the pick carries the tag through onChange into ComposeWindow state and the send request's from_tag_id, and the collapsed From row chip reads auto with the tag title while the resolved-signature preview resolves to the best mailbox within the tag 2026-07-20 05:28:05 +02:00
Matthew Meszaros c53225e6f3 feat: undo send in the dashboard: instant compose and reply sends feed a new outbox store and an amber header pill counting down to the server scheduled_at with inline Cancel plus a portaled dropdown listing every pending send with per-row cancel, cancelling a compose reopens the docked composer from a full draft seed and cancelling a reply navigates to the thread and reopens the reply composer prefilled via a restore handoff, 404 collapses to Already sent, and the profile settings page gains an autosaved Undo window NumberInput (5-120s) wired to PUT /auth/me/send-preferences patching the cached /auth/me user 2026-07-19 18:56:55 +02:00
Matthew Meszaros e46fc1cd3e feat: make the composer picker search feel alive: contact searches debounce 300ms and keep previous results on screen while the next query loads (new useDebouncedValue hook + keepPrevious option on useSearchContacts), the browse panel and From menu ease their height with a ResizeObserver-driven AnimatedHeight instead of snapping, in-flight searches show a spinner in the search box plus shimmer skeleton rows on first load, FilterMenu closes itself on any outside click or Escape (its trigger lives inside a data-floating panel the shared hook ignores, so opening the sibling filter now closes the other), and the browse panel widens to 400px 2026-07-19 18:36:31 +02:00
Matthew Meszaros 038668ff51 feat: clean up the composer picker chrome: a new compact FilterMenu (quiet bordered trigger showing the active choice, portaled dot-row dropdown) replaces the chip rows — the From menu's tag filter becomes one small All-tags dropdown beside the search box, and the browse-contacts panel collapses its three header rows (search, category chips, sort row) into a single row with search plus category and sort dropdowns, hugs its content height instead of a fixed 380px, shows one category badge per row, and only reveals the Add footer once something is picked 2026-07-19 18:15:56 +02:00
Matthew Meszaros 7eb8510a89 feat: fix the composer's From tag chips and browse-contacts panel: the From menu now offers every defined tag (not just ones already on a mailbox, so a tag created in the bulk bar is immediately selectable), and the browse panel portals to the body with a viewport-anchored fixed position and flip (the compose window clips overflow, so the 380px panel was being cut off inside the To row and reading as a cramped overflowing box), matching the MailboxPicker portal pattern 2026-07-19 17:06:00 +02:00
Matthew Meszaros 02a524616e feat: composer recipient upgrades: a user-plus button on the shared recipient field opens a browse-contacts panel (search, category filter chips, Recent/Name/Email sort, checkbox rows with category badges, already-added rows disabled, Add-N appends with case-insensitive dedupe) available in both compose and reply, and the docked compose window now auto-minimizes to the corner bar when you navigate to another page so the draft follows you without covering the destination 2026-07-19 16:13:58 +02:00
Matthew Meszaros 5fae462bb2 feat: bulk tag mailboxes from the list's selection bar: a Tags popover with an Add/Remove segmented toggle, tag search, checkbox rows with colored dots, an inline Create-tag row (POSTs /tags cycling the editor's palette and updates the profile cache), and an apply button that hits the new PATCH /emails/tags bulk endpoint, invalidates the emails queries, and keeps the row selection for chaining 2026-07-19 16:13:58 +02:00
Matthew Meszaros ccbb6b068c feat: soften the dither charts for the white theme: tones drop from 600 to 500-level hues (slate-900 to slate-600), dither fill dots render at ~84% alpha so they blend into the page while lines and hovered elements stay fully opaque, legend dots match, and the campaign overview legend swaps its dark active pills (which read as a black background with every series on) for the light slate-100/white style the other pages use 2026-07-19 14:17:11 +02:00
Matthew Meszaros 03634358de feat: compose all metrics on one graph: DitherMultiAreaChart draws a smoothed line per series with nested dithered bands (per pixel the smallest containing band wins so overlaps read as layers) and a shared crosshair dot-per-series tooltip; the shared chart becomes MultiTrend, and analytics, campaign overview, and deliverability swap their single-metric switcher for legend toggles with tone dots that show/hide series in place (deliverability starts with sent hidden so failure signals stay readable, and at least one series always stays on) 2026-07-19 14:13:24 +02:00
Matthew Meszaros 71e04c00dd feat: roll dithered charts across the dashboard: the shared DailyBars becomes DailyTrend (smoothed dithered area graph) powering campaign overview, analytics, and deliverability over-time charts with per-metric tones, deliverability provider placement rows use DitherStack, the mailbox warmup chart becomes a dithered bar chart with target ghost bars and tap-to-pin day selection, API-key traffic uses DitherColumns, and the content score, campaign send progress, credits meter, and unibox queue bars all move to DitherMeter 2026-07-19 14:01:53 +02:00
Matthew Meszaros 7502b0f2b4 feat: grow the dither kit for the analytics surfaces: a violet tone, area-chart tooltips anchored to the hovered dot (flipping below near the top edge), ghost reference bars + tap-to-pin selection on DitherBarChart, DitherColumns stacked vertical columns with per-part tones and hover callback, and DitherStack horizontal share bars with a left-to-right reveal 2026-07-19 14:01:53 +02:00
Matthew Meszaros e4ad9e3661 feat: make billing tabs real URL paths: the route becomes billing/:tab? with /app/settings/billing/plans, /ai-credits, and /payment slugs (unknown slugs normalize to the overview), Stripe checkout and topup return URLs point at the tab they left from, the header credits meter deep-links to /ai-credits, the settings topbar subtitle matches nested billing paths, and the document title covers the sub-paths 2026-07-19 13:50:03 +02:00
Matthew Meszaros f0b9c5f49c feat: add DitherAreaChart to the dither kit: a monotone-cubic smoothed line with a dithered area fill fading to the baseline, left-to-right draw-in reveal, and a hover crosshair with dashed guide, line-anchored dot, and tooltip — a real graph, not bars 2026-07-19 13:50:03 +02:00
Matthew Meszaros 587bda2039 feat: split the billing settings page into browsable tabs (Overview, Plans, AI & credits, Payment) with an animated sky underline and per-tab fade transition, sync the active tab to ?tab= so Stripe topup returns land back on the AI tab and the header credits meter deep-links to it, route the current-plan button to the Plans tab, and render the usage rows with dithered meters and animated counts 2026-07-19 13:39:07 +02:00
Matthew Meszaros 98cb220147 feat: add hand-rolled dithered canvas primitives in web/src/components/ui/dither.tsx: Bayer-4x4 ordered-dither bar chart with staggered entrance and hover tooltip, progress meter with a dithered leading edge, donut ring with a dithered sweep tip, a pointer+keyboard slider on a dithered track, and a rAF AnimatedNumber — the dither-kit look with zero new dependencies and prefers-reduced-motion fallbacks 2026-07-19 13:39:07 +02:00
Matthew Meszaros f7a1027367 feat: add workspace-shared assistant history: a manage_settings toggle on the organization (assistant_shared_history, off by default) makes every conversation visible to and continuable by every member with the use-AI permission — the service resolves a session to its owner's rows so open/continue/approve/delete all work cross-member while credits still charge the acting member, the org-wide listing joins owner names for attribution in the history rail, the workspace settings page gains the AI assistant section with the toggle and an explicit exposure warning, and the assistant guide's access and privacy sections describe both modes 2026-07-19 13:22:56 +02:00
Matthew Meszaros 7ef7d90d8c feat: introduce the Use AI organization permission (bit 15, the last of the 16-bit mask) gating every AI surface: assistant session routes, generation write/edit, and the compose and reply draft endpoints get a member-permission layer (API keys re-check their existing bit), migration 000073 backfills the bit onto all existing roles and member overrides so nobody loses access, defaults grant it to admin and manager but not viewer, and the web mirrors it in the permission catalog with gating on the header spark button, Cmd+I, the panel itself, and the AI drafting caret (plus team-roles and endpoint-map docs) 2026-07-19 11:04:58 +02:00
Matthew Meszaros bacbf5552f feat: upgrade the assistant's full-screen history rail: conversations group under Today/Yesterday/This week/Earlier sections, a search field filters them by title, each row gets a hover (always-visible on touch) delete action behind the in-app confirm that also closes the conversation's open tab and invalidates the list, the New chat button shows its Alt+N kbd chip, and the guide documents deletion and search 2026-07-19 10:50:33 +02:00
Matthew Meszaros 8eb2824ac4 feat: render the assistant's stream like a live agent and surface its keybindings in the panel: text_delta chunks append to a live-flagged trailing block that the closing full-text event replaces, the trailing block of a running turn types itself out through a rAF SmoothText typewriter with the blinking caret so even chunky providers read as continuous generation, and the empty state gains kbd chips for toggle, new chat, pop out, and minimize plus an all-shortcuts link into the shortcuts dialog 2026-07-19 10:42:10 +02:00
Matthew Meszaros ab938575d3 feat: give the AI assistant a floating window mode on desktop: pop out via the header button, Alt+P, or tearing the docked header off its edge, then drag it anywhere by the header and resize from any edge or corner (360-920 wide, min 400 tall, always clamped fully on screen), with the geometry persisted alongside side and width, a dock-back button targeting the remembered edge, and the assistant guide updated 2026-07-19 10:31:20 +02:00
Matthew Meszaros b866d2b99d feat: harden the From menu's tag filter and search field: tag chips now derive from the store's mailbox directory sorted by position instead of the candidates fetch (so they show even while candidates load), the search input gets a real slate border instead of the invisible white-on-white fill, and the panel clamps against clientWidth so its right edge cannot slip under a scrollbar 2026-07-19 10:21:25 +02:00
Matthew Meszaros b06be51783 feat: raise the shared tooltip layer from z-50 to z-150 so hover tooltips (like the AI caret's Write with AI hint) render above the docked compose window at z-70 instead of hiding behind it, while staying under the confirm dialog at z-200 2026-07-19 10:21:25 +02:00
Matthew Meszaros 018ce89b79 feat: make the recipient autocomplete category-aware in both composers: suggestion rows show each contact's category chips with their colors, typing a category name offers it as a filter row (enter or click applies it as a removable chip in the field, backspace or escape clears it), and an active category narrows the suggestions to that group via the search endpoint's category_ids 2026-07-19 09:26:02 +02:00
Matthew Meszaros c3977d30d4 feat: give the reply composer's To/Cc/Bcc fields the same contact autocomplete as compose by swapping the plain chip editor for the shared ContactRecipientField (type to search existing contacts by name, email, or company; raw addresses still commit with Enter) and delete the now-unused local RecipientField 2026-07-19 09:22:17 +02:00
Matthew Meszaros 1da0a932fa feat: shrink the From mailbox menu into a compact searchable list: single-line h-8 rows (auth dot, address, history count, sent/limit budget, best marker) with the name and score reasons moved into the tooltip, a search box that filters as you type, tag filter chips built from the org's mailbox tags (only tags actually in use are offered), an inline Auto row, and a no-match empty state 2026-07-19 09:22:17 +02:00
Matthew Meszaros 4bbbf57bb2 feat: serialize the AI draft bar's phase transitions with AnimatePresence mode wait so the clarifying-question card no longer enters beside the exiting busy pill, gets shoved off-center, and jumps back when the pill unmounts 2026-07-19 09:16:55 +02:00
Matthew Meszaros a3339fe0b3 feat: update the reply composer's layout doc comment to describe the new target strip and row order 2026-07-19 08:56:21 +02:00
Matthew Meszaros 50701af000 feat: align the reply composer's horizontal rhythm: body textarea and signature preview blocks share the px-4 left edge with the header rows instead of drifting between px-5 and mx-3/mx-5 2026-07-19 08:56:08 +02:00
Matthew Meszaros 3c4738e065 feat: rebuild the reply composer header rows in the compose window's language: To first with quiet Cc/Bcc toggles on its right, plain inline labels with hairlines instead of the uppercase label lane and vertical divider column, the From mailbox as calm text without the boxed mono chip, and an unlabelled slightly heavier subject line 2026-07-19 08:55:40 +02:00
Matthew Meszaros 5c3b6aab10 feat: replace the reply composer's target-message preview card (round icon badge, uppercase kicker, three stacked lines) with the compose window's quiet single-line bar: Reply/Forward to name and subject on a slate-100 strip with a muted corner icon (violet when forwarding) and a ghost close button 2026-07-19 08:54:35 +02:00
Matthew Meszaros 6f8db3ae05 feat: de-template the cold-email humanizer (drop the quotable example asks and subject that models parroted verbatim, add a VARIATION section mandating a fresh skeleton per email and capping punchy standalone lines at one) and add compose drafts: autosaved per-user working copies (client-generated ids, idempotent PUT on a 1.2s debounce, migration 000072) with Saving/Saved in the window header, close-keeps-draft instead of the discard confirm, delete on send or when emptied, and a Drafts list under the rail Compose button to resume or delete, documented in the guide and endpoints scope map 2026-07-19 08:52:56 +02:00
Matthew Meszaros e6b11f8d07 feat: fix compose draft quality and review chrome: a compose-specific humanizer frame (BuildComposeRules) that bans copywriting rhythm outright (no standalone punch lines, no problem-agitate-pitch, no market generalizations, 40-70 words, plain first sentence) and gives no example phrasings for the model to parrot, clear the grounding report and voice-profile nudge when the draft flow ends instead of lingering after Keep, restructure the Draft ready card into breathing rows (title plus usage, grounding line, right-aligned action row), and portal the From mailbox menu to the body with viewport-aware flip so the compose window's overflow clipping can't cut it off 2026-07-19 08:45:47 +02:00
Matthew Meszaros 6ce5ebe900 feat: make compose AI drafting grounded and agentic: POST /unibox/compose/draft assembles the recipient's contact 360, the full correspondence history with the address, and the org voice profile into the prompt (2-credit minimum, reason compose_draft, usage settle, refund on provider failure), and when the purpose is genuinely unknowable the model returns a clarifying question instead of inventing a pitch; the draft bar gains a question phase (answer inline and it writes), the review card reports exactly what the draft was grounded in, and the composer nudges to Settings > Workspace with a link when no voice profile is set; docs cover the endpoint, credits row, and guide behavior 2026-07-19 08:37:39 +02:00
Matthew Meszaros 0c4e63ab01 feat: make the compose window behave like a real docked draft: minimize to a compact corner bar (draft state stays mounted while you browse other pages, header click or chevron restores, n or any openCompose restores instead of clobbering the draft), a serious single-icon header that reads New email to <name>, <company> as the recipient resolves, and the body textarea aligned to the same horizontal padding as the subject and header rows 2026-07-19 08:31:53 +02:00
Matthew Meszaros a5982808fd feat: stop the inbox flashing on scope and route switches by keeping previous unibox search pages as placeholder data while the new key loads (no more full-pane skeleton flash) and holding overview counts in cache for 30 minutes so the header stats stop rolling up from zero, and flatten the compose header to a quiet single-line bar that shows just the recipient (or New email) with the icon chip and subtitle removed 2026-07-19 08:25:06 +02:00