Commit Graph

203 Commits

Author SHA1 Message Date
Matthew Meszaros 1387541bf0 feat: make self-hosted auth work without a mail relay by rewriting the platform SMTP transport with real AUTH and TLS (it did neither, so SMTP_USERNAME/SMTP_PASSWORD were dead and every documented relay was unreachable), adding MAIL_TRANSPORT=smtp|log|ses with a log transport that prints codes so a fresh install can sign in with no relay, demoting the emailed login code to AUTH_LOGIN_CODE=always|new_device|off (off on self-host, per NIST SP 800-63B and OWASP ASVS), claiming the first owner through a single-use setup link or WARMBLY_BOOTSTRAP_* instead of register-then-psql, deriving every emailed URL from APP_URL rather than a hardcoded app.warmbly.com that leaked live reset tokens to the vendor, fixing the confirm hooks that read path params against paramless routes and broke login, register and reset confirmation in the dashboard everywhere, adding generic OIDC with PKCE, one-time state, verified nonce and (issuer,subject) identity binding, enforcing 2FA on the social paths that skipped it, adding a per-IP limiter and trusted-proxy handling to the unthrottled auth group, refusing boot on the published default secrets, and dropping mailpit from the default stack 2026-08-14 10:37:14 +02:00
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 443dcbf4b5 Merge pull request #83 from warmbly/ai-content-blocks
AI content blocks and a much more capable dashboard assistant
2026-07-22 17:59:36 +02:00
Matthew Meszaros 5bb449f682 feat: add the POST /generation/ai-variable preview endpoint that resolves a block for a sample or selected contact with the same credit semantics as write 2026-07-22 17:05:08 +02:00
Matthew Meszaros d25f635b72 feat: add ListCustomFieldKeys to the contact service and repository plus the GET /contacts/custom-fields handler returning the org's distinct contact custom-field keys 2026-07-22 17:04:51 +02:00
Matthew Meszaros f768029ec0 feat: automate warmup conversation generation, coherent replies, adaptive rotation, and admin observability 2026-07-22 12:11:19 +02:00
Matthew Meszaros b465f30d42 fix: scope account analytics by org id not user id 2026-07-21 17:34:59 +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 8de314ae8b feat: serve public blobs from the backend and fix avatar uploads on filesystem 2026-07-20 09:56:02 +02:00
Matthew Meszaros 85ab4ad7b5 feat: replace the notification email cadence enum with a configurable bundling window and drop the instant mode entirely: email_digest_minutes (30 minute floor, 30 default, 1440 max, constants in config) replaces email_digest with no per-event option or NOTIFICATION_EMAIL_ALLOW_INSTANT escape hatch, the handler validates the range and email_delivery now returns min/max minutes so clients render the bounds, the web settings page offers window presets (30m/1h/3h/daily) plus a Custom minutes NumberInput and iOS swaps the cadence menu for window presets including the server value when it matches none, the repo clamps stored values on read, and the guide plus deployment guide describe the window and keep only the NOTIFICATION_EMAIL_DAILY_CAP env 2026-07-20 08:01:22 +02:00
Matthew Meszaros 682ef3ac24 feat: cost guards on the notification email channel: the per-event instant cadence becomes self-host opt-in (NOTIFICATION_EMAIL_ALLOW_INSTANT, default off - PUT rejects it, stored values read back as smart, holds degrade to smart) with the capability exposed as email_delivery on the preferences GET so web and iOS hide the option on hosted deploys, each user gets a rolling 24h budget of non-security notification emails (NOTIFICATION_EMAIL_DAILY_CAP, default 25, 0 unlimited) counted off sent rows with over-budget alerts skipped to the in-app feed while security sign-ins always send, and coalesced group emails re-verify org membership at flush time so a member removed during the hold is dropped from To; deployment guide documents the three envs and the guide notes both limits 2026-07-20 07:44:43 +02:00
Matthew Meszaros a534ef1908 feat: notification email channel becomes digest-first: email-channel notifications queue as pending rows (migration 000076 adds group_key/email_state/email_due_at/email_attempts) with a due time from a new per-user email_digest cadence (instant/smart 15m/hourly/daily, security sign-ins always immediate), a 30s flush loop with SKIP LOCKED claims bundles a user's pending rows into one digest email and coalesces org-shared group_key events into a single email with every recipient in To, reading a notification in-app cancels its pending email, and NotifyOrg targets only members holding a permission (Slack fires once per group) with new producers: dead-worker downtime to manage_emails members (SetNX-deduped per incident), trial expiry to manage_billing members via new billing_alert category (replacing the direct owner email), and invitation accepts to manage_team members via new team_activity category 2026-07-20 07:16:25 +02:00
Matthew Meszaros f20300a280 feat: tag-scoped auto mailbox pick on compose: POST /unibox/compose accepts from_tag_id which restricts the automatic sender choice to active mailboxes carrying that tag (best-with-budget-first within the group, 400 when the tag has no active members, ignored with an explicit account; GetByTags is user-scoped so foreign tag ids resolve to no members) 2026-07-20 05:28:05 +02:00
Matthew Meszaros db400ac086 feat: undo send window on the backend: migration 000075 adds users.undo_send_seconds (default 30, CHECK 5-120), emailsend queues instant sends (compose, reply, agent-draft approvals) that many seconds into the future so the existing DELETE /unibox/scheduled/:task_id cancel path can still stop them, the value rides /auth/me and a new JWT-only PUT /me/send-preferences updates it with bounds validation and user-cache invalidation 2026-07-19 18:45:50 +02:00
Matthew Meszaros e1750d8988 feat: add PATCH /emails/tags for bulk mailbox tagging: add_tags/remove_tags set semantics across up to 1000 mailboxes in one transaction (ownership of both mailboxes and tags enforced in SQL so stale ids are skipped, composite-PK insert makes re-adds no-ops), naturally idempotent so no Idempotency-Key needed, one audit entry with account/added/removed counts riding the email_account spine, and the endpoint registered in the docs scope map under WRITE_EMAILS 2026-07-19 16:13:43 +02:00
Matthew Meszaros 68e3ca6b6b feat: extend the per-member AI limit to all three windows: member_limit_daily and member_limit_weekly join the monthly ceiling on org_ai_settings, MemberSpentInWindows sums the acting member's debits per window in one query, checkSpendLimits enforces day/week/month member ceilings with window-specific 429 messages, and the billing card's Per-member limits row grows the same three fields as the workspace caps 2026-07-19 13:22:56 +02:00
Matthew Meszaros 458234cd5d feat: add clear-all assistant history: DELETE /ai/sessions wipes every conversation the member owns in the workspace (transcripts cascade, count returned, audited as an ai_session delete with clear_history metadata), the history rail gains a Clear history footer action behind the in-app confirm that also closes all session-backed tabs, and the assistant guide documents access gating, per-member limits, privacy, and both delete paths 2026-07-19 11:05:18 +02:00
Matthew Meszaros f4014b8b73 feat: add a per-member monthly AI credit limit to the org spend controls: new member_limit_monthly on org_ai_settings flows through the settings repo, PATCH endpoint, and billing UI (its own Per-member limit field on the AI usage card), and checkSpendLimits enforces it by summing the acting member's debits this calendar month via the ledger's actor attribution (new MemberSpentSince query on the partial index from migration 000073), returning a 429-mapped member-limit error while unattributed scheduled work stays exempt 2026-07-19 11:05:18 +02:00
Matthew Meszaros 2de973df68 feat: add DELETE /ai/sessions/:id so members can remove an assistant conversation: repository delete scoped by org and user with the transcript cascading via FK, not-found on zero rows, an ai_session delete audit entry (which the existing realtime spine turns into live history refreshes), and the endpoint registered in the docs scope map 2026-07-19 10:50:33 +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 097664b673 feat: add a direction filter to unibox search (?direction=sent|received) resolved against the org's own mailbox addresses so the dashboard can show a real Sent view and the compose history panel can split sent mail from replies 2026-07-19 07:28:38 +02:00
Matthew Meszaros cf58c6d1d2 feat: add the compose backend: POST /unibox/compose sends a brand-new outbound email with org-wide recipient suppression enforced and optional auto mailbox selection, GET /unibox/compose/candidates scores every active mailbox for a recipient (conversation affinity from unibox history plus queued email tasks, remaining daily budget from daily_email_counts, domain-auth health) with human-readable reasons, a recommended pick, contact resolution, and suppression state 2026-07-19 07:27:23 +02:00
Matthew Meszaros 666a137cd8 feat: add an address filter to unibox search that matches either side of the exchange (from_addr OR to_addr) so the compose history panel can show every conversation with a contact, exposed as ?address= on GET /unibox, with a GIN index on to_addr (migration 000071) mirroring the existing from_addr index 2026-07-19 07:27:23 +02:00
Matthew Meszaros 1823f03a80 feat: live usage-based credit feedback — every fresh debit publishes BILLING_CREDITS_CHANGED through the creditwatch monitor so the header meter counts down in real time (AnimatedNumber tween, plus client-side invalidation on write/edit/draft success), generation responses now return the real credits_charged and tokens_used from the usage settle and every AI surface shows the true cost instead of flat labels, and the composer AI became overlay-based: the draft bar floats over the body instead of pushing layout, a sky sheen sweeps the input while the model writes, and the selection being rewritten is painted with pulsing highlight rects that grow as the rewrite types in 2026-07-18 16:30:05 +02:00
Matthew Meszaros 5178acd874 feat: in-composer AI writing experience — select text in the unibox reply textarea or the campaign TipTap editor and a floating Edit-with-AI pill opens quick actions (improve, shorten, expand, fix grammar, friendlier, more formal) plus free instructions backed by a new fenced /generation/edit endpoint (1 credit, idempotent, refund on failure, prompt-injection fencing around the passage), rewrites type themselves in with undo/again/done review, and Draft reply now runs through an inline draft bar with staged shimmer status and Keep/Adjust/Retry/Discard instead of a toast 2026-07-18 11:55:37 +02:00
Matthew Meszaros 4f9e6e6282 feat: full attribution on every AI credit charge — new actor_user_id + context jsonb columns on the transaction log (migration 000070), a typed models.CreditContext carried via request context (models.WithCreditMeta) so base charges, usage settles, web-search fees, and refunds all inherit it without signature churn, wired at every spend site (campaign switches record campaign/step/contact, automation nodes and Ask AI record automation/node/run and the question asked, reply drafts and inbox agent record the thread, the dashboard agent records the session, research records contact/run, and manual features record the triggering user), rendered as a detail line with token counts in the billing transaction log with corrected reason labels, and documented in the AI credits guide 2026-07-18 08:52:16 +02:00
Matthew Meszaros 053ca6a71d feat: usage-based AI credit system — every AI call reserves its flat minimum then settles the real token cost per model (light 1500/standard 400 tokens per credit, drain-to-zero overage settle wired into writing assistant, reply drafts, dashboard agent, inbox agent, research, automation nodes, and campaign switches), org spend controls in a new org_ai_settings table (day/week/month hard limits enforced in Consume, low-balance alert threshold, auto top-up config), a credit-watch monitor hook that fires BILLING_CREDITS_LOW realtime alerts once per day and buys the configured pack off-session via a new Stripe AutoTopUpCredits (idempotent on the PaymentIntent, bounded per month, Redis-locked), GET /subscription/credits/usage + GET/PATCH settings endpoints, and an AI usage & spend controls billing card (spend vs limits, 30-day chart, per-feature and per-model breakdowns, limits/reminder/auto-top-up form) with a realtime low-credit toast 2026-07-18 08:39:41 +02:00
Matthew Meszaros d0e8bc1c97 feat: add one-command MCP OAuth connect on api.warmbly.com/v1/mcp — RFC 7591 dynamic client registration for public PKCE clients (dcr.go), RFC 9728 protected-resource metadata + WWW-Authenticate challenge via MCPAuthMiddleware, /v1/mcp now accepts an API key or OAuth token, public-client auth with no secret and mandatory PKCE reusing the existing OAuth 2.1 server (nullable-org clients, migration 000066), executable-redirect-scheme hardening on the open register endpoint, plus mcp/oauth/authentication/endpoints docs 2026-07-16 08:57:19 +02:00
Matthew Meszaros e64b0b161d feat: sweep stale OPENAI_API_KEY/AI_LOCAL_MODEL mentions out of comments, the provider-not-configured and warmup-admin error strings, env.example, and the deployment guide now that AI_* is the only config surface 2026-07-16 06:39:07 +02:00
Matthew Meszaros 44797191b8 feat: skip credits for reply drafts when running on a free/local model 2026-07-15 18:52:07 +02:00
Matthew Meszaros 18a98944ae feat: skip credits for the writing assistant when running on a free/local model, returning the unchanged balance 2026-07-15 18:52:07 +02:00
Matthew Meszaros 59044aedf2 feat: the dashboard agent runs un-metered on a free/local model, streams a free_model signal, and injects the org voice profile into its system prompt so its writing sounds human 2026-07-15 18:51:51 +02:00
Matthew Meszaros 81bfb41d4e feat: add GET /ai/sessions/:id/messages returning a session transcript hydrated into the client turn and block shape plus any pending approval, so a reopened assistant conversation rehydrates 2026-07-15 17:54:16 +02:00
Matthew Meszaros 56de65380b feat: inbox agent that drafts a suggested unibox reply on inbound human replies for human approve/edit/discard - paid + per-org opt-in (organizations.inbox_agent_enabled) feature where the consumer's reply hook, on a non-automated reply, detaches a goroutine (panic-contained, never blocks ingest) that checks entitlement, dedupes via ai_thread_drafts partial unique indexes (one pending per thread, unique source_message_id), pre-checks balance, grounds a reply in the thread history + org voice + skills via generation.Provider.Complete, reserves a draft row then charges 5 credits (idempotency inbox_agent:<draft.ID>, row unwound on a fresh context if the charge fails so no unpaid draft lingers), and emits an org-scoped AI_DRAFT_READY event gated on access_unibox; the agent never sends - only a human POST to /unibox/agent-drafts/:id/approve sends through the normal reply path, claiming pending->approved before send with an approved->pending revert on send failure, alongside list + discard endpoints; plus the unibox awaiting_agent_draft badge + agent_drafts search scope, an AgentDraftCard in the thread view with inline-editable approve-and-send/discard, the workspace settings opt-in toggle, CanUseInboxAgent paid gate, migration 000065, and docs (inbox-agent guide, endpoints, realtime) 2026-07-14 06:13:27 +02:00
Matthew Meszaros 24a932c439 feat: Warmbly MCP server exposing the tool registry to any MCP client at /api/v1/mcp - a streamable-HTTP JSON-RPC endpoint (initialize/tools/list/tools/call/ping) authenticated by API key, where tools/list reflects only the static tools the key's permission mask allows and tools/call runs them gated by each tool's RequiredAPIPerm, send-class tools are never exposed or callable, per-key rate limits and usage logging apply, and the org's own connected MCP tools are not re-exposed; plus api/mcp.mdx documenting the connection url, bearer auth, the tool catalog, and Claude Code/Desktop and Cursor client configs, linked from endpoints and authentication 2026-07-13 20:11:21 +02:00
Matthew Meszaros 2aec6745fa feat: connect external MCP servers whose tools the AI assistant can use - ai_mcp_servers table with bearer tokens sealed by the org DEK cipher (never returned) and SSRF-validated https urls, a dependency-free streamable-HTTP JSON-RPC client (initialize/tools/list/tools/call over safehttp dial-time IP blocking with SSE+JSON handling and body caps), an mcp service that discovers tools on connect and contributes only enabled servers' tools to the dashboard agent as namespaced mcp_<server>_<tool> defs that are always write-class and never auto-allowed, a registry DynamicToolSource hook so per-org tools join the agent's tool set and resume executes them through ToolDefs, /ai/connections CRUD gated on manage_settings with an mcp_server audit entity and spine, a Connections settings page to add servers and review/enable discovered tools, and docs 2026-07-13 20:05:24 +02:00
Matthew Meszaros a2f50b6572 feat: org AI skills (playbooks) that every AI feature loads and follows - ai_skills table (org-scoped, unique name per org, 32KB content cap) with a skills service exposing CRUD plus an enabled-skills preamble injected into the dashboard agent, contact research, and reply-draft prompts and a load_skill read-tool that returns a playbook's full content by name, /ai/skills CRUD gated on manage_settings (JWT) or the AI_AGENT scope (API) with an ai_skill audit entity and spine entry, an AI skills settings page (list rows opening a right-side drawer with name, one-line description, enable toggle, and a markdown body), and docs with two example playbooks 2026-07-13 19:46:25 +02:00
Matthew Meszaros f48d51bf16 feat: AI contact research agent that gathers cited web findings per contact with sync and background-batch runs - contact_research_runs table plus a strict save_research schema (every signal and public_artifact must carry a url, confidence high/medium/low, signals<=5 hooks<=3, reject-and-reprompt once then fail), a text/template runtime prompt injecting org voice, contact record, objective, and 5-search/6-fetch budgets, a research service that drives search_web/fetch_url through the provider loop and charges 2 credits on save (billable even for nothing_found, refund-free since charged only on save, balance and abuse-cap pre-checked so a capped org never does free work), POST/GET /contacts/:id/research plus a 500-cap /contacts/research/batch draining through a bounded pool of 4 workers over FOR-UPDATE-SKIP-LOCKED claims (no new Kafka), an org-scoped AI_RESEARCH_PROGRESS realtime event gated to view_contacts, APIPermAIResearch bit 23, a ContactEdit Research tab and a ContactsTable bulk Research action, and docs; also org-scopes every credit and research idempotency key so a client-supplied Idempotency-Key can never replay across tenants 2026-07-13 19:34:11 +02:00
Matthew Meszaros 8d208d8c41 feat: org voice profile grounding every AI writing surface plus context-grounded unibox reply drafts - organizations gains product_description/icp_notes/voice_profile (000059) with a manage_settings workspace settings section, generation.BuildVoiceRules and BuildReplyRules fold the humanizer rules and org grounding into every prompt, WritingGenerator.GenerateWriting now takes a VoiceContext and a provider.Complete primitive powers single completions, POST /unibox/reply/draft assembles thread history plus the counterpart contact (custom fields and campaigns) plus the voice profile into a reply-tuned prompt that charges 2 credits with refund-on-failure and a client idempotency key and never sends, a Draft reply button fills the composer, and docs cover the voice profile and AI reply drafts 2026-07-13 19:04:57 +02:00
Matthew Meszaros 7f425c1624 feat: dashboard-wide AI assistant with streamed tool-use runs, per-action approvals, and per-iteration credits - agent_sessions/agent_messages/ai_tool_policies migration, aiagent service runs the M2 registry through the provider loop over a resumable jsonb transcript, streams text/tool-step/approval/done events over SSE, gates write tools behind approve/deny/always-allow (send always per-action) and charges 1 credit per iteration (budget 20, refund on provider failure, out-of-credits 402 insufficient_credits, cap 429 usage_cap_exceeded) with a resume-safe idempotency namespace, provider PreIteration budget hook, /ai/sessions endpoints (cursor list + two SSE runs) gated by membership with tools bound to the member's org-permission bits, APIPermAIAgent bit 22, ai_session audit entity + spine, and a right-side resizable panel (Cmd+I / sparkle button) with streamed text, collapsible tool steps, inline approval and draft-artifact deep-link cards, stop, new chat, and a credits/budget meter; tenancy enforced at the SQL layer and docs for the assistant, endpoints, permissions, and error codes 2026-07-13 18:46:43 +02:00
Matthew Meszaros 6ad2f13a39 feat: shared AI tool registry (internal/app/aitools) that runs 16 permission-gated tools as the invoking user over existing service-layer calls - Registry+Invocation enforce org-permission bits (JWT) or API-key bits per tool, ToolDefs binds handlers into the provider agent loop and Call powers direct MCP invocation; tools cover contacts (search/get/update/add_tag/remove_tag), CRM (create_task/create_deal), campaigns (list/stats/create_campaign_draft), unibox (list_threads/get_thread/draft_reply, gated on CanUseUnibox, draft never sends), create_automation_draft (disabled), and web search_web + fetch_url (webhook SSRF validator + safehttp dial-block + 2MB cap + html-to-text + 15min Redis cache + rune-safe truncation); every write audits via AuditService.LogAction so the spine refreshes teammates, registry built once in main and exposed on the handler for M3/M8 2026-07-13 18:15:58 +02:00
Matthew Meszaros e07c33d3cd feat: provider-agnostic LLM agent-loop layer (OpenAI-first, Anthropic self-host connector) with RunAgent tool-use loop, approval/resume contract, pluggable web search, and extracted voice-rules builder - generation.Provider.RunAgent executes Go tool handlers over a resumable transcript with a read-auto/write-approve gate, openAIProvider drives it via a lean HTTP client retargetable with OPENAI_BASE_URL for any OpenAI-compatible endpoint and also serves the writing assistant, anthropicProvider mirrors it with tool_use/tool_result multi-turn plus hosted web_search, NewProvider prefers OPENAI_API_KEY, SearchClient supports Serper/SearXNG with a clean not-configured error, and BuildVoiceRules extracts the humanizer prompt for reuse; refreshed stale model ids to sonnet-4-6 and haiku-4-5 2026-07-13 17:52:09 +02:00
Matthew Meszaros 5d6ed2a068 feat: AI credit economy with monthly plan grants, non-expiring purchased top-ups, and Stripe billing - two-pool credit_ledger (monthly allowance reset each cycle on invoice.paid, purchased pool via checkout mode=payment fulfilled webhook-only), consume drains monthly-then-purchased, 3 fixed top-up packs, /subscription/credits balance+checkout+paginated transactions gated on manage_billing, one-time trial grant, billing credits card with balance ring and pack buttons, credit_purchase/credit_grant audit entities and spine, wired plan.monthly_credits through pg_plan reads/writes, retry-safe webhook idempotency, and an AI credits guide 2026-07-13 17:40:20 +02:00
Matthew Meszaros b9a99f91a9 Merge pull request #67 from warmbly/feature/ios-realtime-steps-preview
iOS realtime parity, richer sequence preview, and mobile push notifications
2026-07-13 16:15:18 +02:00
Matthew Meszaros d6b7885e8b feat: mobile push notifications end to end - APNs provider-token client, device_tokens table with session-scoped register/delete endpoints, a push channel in notification preferences (web + iOS toggles), and Redis-backed immediate-then-digest batching (first event pushes now, bursts summarize when the 5h window closes) wired in backend and consumer, with iOS registration/badge sync and docs for the channel, endpoints, and APNS_* deploy env 2026-07-13 16:11:15 +02:00
Matthew Meszaros 262d25779c feat: realtime admin dashboard + diagnosability - new Elixir admin:platform channel (JWT-only, gated on users.admin_permissions) mirrors every platform event, the admin app connects through a /getaway-tokenized Phoenix socket client with a throttled react-query invalidation spine, a Live Events firehose page streams the platform with filters/pause/JSON expand, a System Status page runs parallel infra probes (postgres/redis/kafka/schema-registry/realtime/tracking via /admin/system/status), and the worker logs viewer gains follow mode, line-count selection, and copy 2026-07-12 09:00:10 +02:00