=== Vestly.ai ===
Contributors: ristotoldsep
Tags: chatbot, ai, chat widget, lead capture, customer support
Requires at least: 6.4
Tested up to: 7.1
Requires PHP: 8.1
Stable tag: 0.9.4
License: GPLv2 or later

AI chat for your website that answers from your own pages, prices and products, and captures leads and bookings.

== Description ==

Vestly.ai adds a floating chat widget to your WordPress site, answered by an AI (Anthropic Claude, OpenAI GPT or Google Gemini — your choice, your API key) that is grounded in *your* site's content:

* **Knows your business** — indexes your pages, posts and WooCommerce products into a knowledge base; imports `llms.txt`; themes can inject structured business data via the `rtac_context` filter. The AI is instructed to stay on topic and never invent prices.
* **Quick replies** — scripted lead-capture flow with tappable answer chips (name → contact preference → email/phone/booking link), plus AI-suggested quick replies after every answer.
* **Conversation history** — every chat is stored; browse and read full transcripts in wp-admin, filter by month/language/leads.
* **Lead capture** — collects name, email/phone or routes visitors to your booking calendar; email notification on every lead.
* **Multilingual** — widget UI and flows in Estonian, English and Russian out of the box (WPML-aware); the AI answers in the visitor's language.
* **Tiers** — Starter/Regular/Premium usage caps (conversations per month), model-per-tier mapping, white-label on Premium.
* **GDPR** — IP addresses stored only as salted hashes, configurable retention with daily purge, WordPress personal-data export/erase integration.

== Installation ==

With Vestly Cloud (recommended, the AI is included in your plan):

1. Download the plugin from https://vestly.ee/wordpress and upload it in Plugins → Add New → Upload Plugin, then activate it.
2. In the Vestly dashboard (https://app.vestly.ee) open Chatbots → Install → WordPress and press "Create WordPress keys".
3. In WordPress open Vestly.ai → Settings → Vestly Cloud, paste the cloud address, the bot key and the secret key, tick "Run the chat on Vestly Cloud" and save.
4. Press "Push knowledge now". The chat appears on your site; conversations and leads are in the Vestly dashboard. Updates arrive in Dashboard → Updates.

On its own (your own AI key):

1. Upload and activate the plugin.
2. Go to Vestly.ai → Settings → Providers, paste an API key and press "Test connection".
3. Go to Vestly.ai → Knowledge Base and press "Reindex site now".
4. Configure appearance, welcome messages and the lead flow.

== Changelog ==

= 0.9.4 =
* Connected sites need no licence key: the plan, white label and one-click updates come from Vestly Cloud through the bot's secret key. The licence key remains for sites that run on their own AI key.
* White label follows the plan from Vestly Cloud (connected or licensed), not the manual tier setting.
* The plugin asks Vestly Cloud for updates only when it is connected or has a licence key; a stand-alone site makes no calls home.
* New keys or a new licence are checked right after saving instead of the next day.
* The update offer includes "Requires at least" and the Vestly icon.

= 0.9.3 =
* Vestly moved to its own domain: the default cloud address is https://app.vestly.ee, "Powered by Vestly.ai" and the plugin homepage link to https://vestly.ee. Sites connected to the old address keep working.

= 0.9.2 =
* window.Vestly.ask(text) opens the chat and sends the text as the visitor's question, for "try these questions" buttons on a host page.
* Vestly.ai logo and mark (assets/brand), and the mark as the admin menu icon.

= 0.9.1 =
* Connected mode can be tested on a live site first: "Only logged-in administrators" runs the chat on Vestly Cloud for administrators while visitors keep the local chat and new leads keep arriving in this WordPress.
* Fresher cloud knowledge: the push also runs daily and ten minutes after content is saved or deleted (one push per burst of edits), so theme context such as upcoming course dates no longer waits for the weekly reindex.
* Host pages can open the chat: window.Vestly.open() / close(), or dispatch a "vestly:open" event, e.g. from a "Chat with us" button.
* "Powered by Vestly.ai" and the plugin homepage link to the product page on rixio.ee (Estonian page for Estonian chats, English otherwise).

= 0.9.0 =
* Vestly Cloud connected mode (Settings → Vestly Cloud): the chat runs on the hosted service with the AI included in the plan, conversations and leads live in the cloud dashboard, and this site pushes its indexed pages, products, header and footer, manual entries and rtac_context blocks there after every full reindex (or on demand). Local mode remains the default.
* License activation: with a license key from the cloud dashboard the tier follows the subscription through an Ed25519-signed entitlement, refreshed daily and verified with the public key the plugin ships with (Vestly\Core\Tiers\Entitlement). Without a key the manual tier applies as before.
* Updates from the Vestly Cloud update channel appear in Dashboard → Updates for licensed sites.
* The widget can run on any website: without RTAC_CFG it loads its configuration from the hosted API and renders in a Shadow DOM when the hosted build inlines its stylesheet. Widget texts moved to core (WidgetStrings) for the plugin and the hosted service.
* Provider requests time out after 30 seconds (was 60) and are not retried once the visitor has waited 20 seconds, so an overloaded provider no longer keeps a visitor waiting for minutes.

= 0.8.0 =
* "Ask your bot": ask any question exactly as a visitor would and see the answer from the live prompt and model, which knowledge went in (always included, site context, matched this question), what did not fit, and how full both budgets are. Nothing is stored.
* Golden questions: up to 25 questions with "must contain" / "must not contain" phrases and an expected button, checked every night at 03:00 against the live bot; one email to the alert address when an answer starts failing or passes again. Schema v7.
* Setup wizard (opens once after the first activation without an API key): connect a provider, see what the bot knows about the site (pages, header/footer, prices, contact, booking calendars, llms.txt) with the gaps explained, try a question, name and colour, go live.
* The prompt builder reports what it included and dropped; the chat and the preview share one prompt-context factory.

= 0.7.0 =
* Provider overload no longer reaches visitors as often: up to two retries with exponential backoff and jitter, honouring Retry-After, within four seconds; the last try can use a fallback model (Settings → Providers). The transcript records which model answered.
* Failure alert: after three provider failures within ten minutes one email goes to the alert address (default: the site admin), quiet for six hours, and a "working again" email after the next answer.
* Weekly summary on Monday mornings to the lead recipients: conversations, leads (and how many still wait), booking calendar opens, phone/email clicks, rating, errors and the questions the bot could not answer. Weeks without activity send nothing.
* Quality flags with a reason: the model was unsure, the visitor disagreed, asked the same again, or rated the chat 1–2 stars. The Unanswered page shows and filters by reason; an optional instant email for low ratings.
* Conversion events: booking-calendar and phone/email clicks are recorded (new events table, schema v6) and counted on the dashboard ("Bookings started from chat", a third series in the 14-day chart). The page gets vestly:booking_click, vestly:contact_click and vestly:lead browser events, and GTM gets vestly_* dataLayer events when statistics consent allows (Complianz or window.vestlyConsent()).
* Page-specific openers (Flows page): per page, a different first message and an optional question button in every language.
* The admin is translated to Estonian; the .pot is regenerated. Lead emails link to the lead in the inbox.

= 0.6.0 =
* Lead inbox: every visitor who leaves contact details becomes a lead with a status (new, contacted, booked, lost, spam), your own note, a follow-up date and a history of every change. Search, filters (language, date, follow-up due), bulk actions and CSV export for Excel (UTF-8 with BOM, semicolon-separated). The menu shows how many leads are new; the lead email links straight to the lead.
* Lead view with one-click call, WhatsApp and email, the reason the visitor gave, the page and campaign they came from (UTM tags, Google/Meta/Microsoft/TikTok ad click ids) and the conversation while it is kept.
* Leads have their own table (schema v5, existing leads migrated automatically) and their own retention, 24 months by default, so they outlive the 90-day chat retention. Spam is deleted after 30 days.
* Roles that may work leads are chosen in Settings → Lead capture; such a role sees only the inbox.
* The privacy exporter and eraser cover leads.
* The inbox is a Regular and Premium feature (tier key lead_inbox); on Starter, leads are still saved and emailed.

= 0.5.1 =
* Unpublished pages leave the knowledge base reliably. Indexing on save now reads the post status from the database instead of the hook's post object (WPML re-saves translations while syncing a status change, and a stale object re-indexed pages that had just been set to draft), and every full reindex removes rows of posts and products that are no longer published.

= 0.5.0 =
* Retrieved knowledge has its own budget (new setting "Retrieval budget", default 6 000 characters), separate from the context budget. On pilleriin.com pinned llms.txt sections and the theme context filled 11 544 of 12 000 characters, so not one indexed page reached the model and the bot denied a −10 € newsletter offer that the site advertises.
* Better search: boolean mode with stem prefixes (kinkekaart* also finds kinkekaardi), et/en/ru stopwords, natural-language fallback, and a short follow-up ("aga palju see maksab?") searches together with the previous question.
* Site header and footer are indexed on every full reindex, one row per language, so promotions and contact details shown site-wide are searchable on any site without site-specific code. Cookie banners, the chat widget, forms and scripts are left out.
* Index hygiene: whitespace collapsed before chunking (about half of every chunk was whitespace), empty chunks skipped, and pages rendered with their global post set up, so blocks that read the current post render as they do on the page. llms.txt sections that are mostly link lists are stored unpinned: still searchable, no longer in every prompt.
* New prompt rules: never say that something does not exist unless the knowledge says so (say "not sure", offer to have it checked and mark the answer unanswered), and live context outranks static summaries such as llms.txt (prices, dates, offers); never offer past dates.
* Visitor pushback ("On ju!", "see pole õige", "that's wrong", "это неправда", or the same question again) marks the previous answer unanswered, so it shows in the Unanswered report. A confident wrong answer never set the flag on its own.
* 88 unit tests.

= 0.4.0 =
* Framework-free core: chat logic, prompt, flows, lead rules, the lead email and the three providers moved into core/ (namespace Vestly\Core) behind small interfaces (storage, settings, HTTP, mail, cache, clock, translation, knowledge). WordPress implements them in includes/class-rtac-core.php. No behaviour change for visitors.
* REST transport: POST /wp-json/vestly/v1/{start,nonce,message,lead,rate,reset} on the same service as admin-ajax, same envelope and stale-token retry. The widget switches with the rtac_widget_transport filter; admin-ajax stays the default.
* bot_id threaded through sessions and the knowledge base (schema v4, migrated automatically; always 1 on WordPress, filter rtac_bot_id). Widget storage keys are bot-namespaced for bots other than 1, and the embedding script tag can set data-api-base, data-bot-id and data-lang.
* The site-specific "store products are not booked" rule left the product prompt; sites that need it put it in their guardrails.
* Rate limit on lead answers per client raised to 120 an hour: mobile carriers put many visitors behind one IP, and email abuse is already capped by one email per session.
* Unit tests (PHPUnit, 65 tests) and a core purity check that fails on any WordPress function or constant in core/; GitHub Actions runs both, plus a PHP 8.1 syntax check. Dev tooling is never deployed.

= 0.3.0 =
* Question-first opening is the new default flow: visitors can type a question straight away and the AI answers it, with optional "Prices and services", "Book a time" and "Contact me" chips. Contact details are asked only when the visitor wants to be contacted, and the reason comes first. On pilleriin.com the old name-first opening lost 19 of 26 visitors, and 4 of the 7 who typed anything typed their question into the name field.
* New "note" field (the reason for contact) stored on the session, shown in the admin, exported by the privacy tools and put in the lead email. Schema v3, migrated automatically.
* The AI's offer_lead now enters the contact branch (flow.contact_start) instead of restarting the flow, skips fields already known and uses the visitor's first question as the reason.
* A question typed at a name prompt is kept with the reason and the name is asked again, instead of being saved as the name.
* A question typed while the chat is still starting is sent once the session exists; previously the greeting wiped it.
* Lead email in the site language, with the reason in the subject, a transcript excerpt and Reply-To set to the visitor. Sent once per session. Opening the booking calendar is no longer a lead and sends no email.
* Several booking calendars: the rtac_booking_links filter returns one button per location, used by the "Book a time" step and by the AI's offer_booking.
* Booking button repeat-guard no longer hides the button when the visitor asks to book again (the "I can help you book" loop with no link).
* The AI's action is stored with each reply and shown in the transcript. The prompt now includes today's date and what the visitor told the flow; retrieved knowledge moved after the cache breakpoint (Anthropic was paying a cache write on almost every turn). New rules: booking via the action only, no action-like quick replies, admit being an AI.
* History sent to providers starts on a user turn and never has two turns of the same role in a row (Anthropic rejected both, which broke every later message in the session).
* Truncated, empty or refused model output shows the friendly error instead of raw JSON or an empty bubble; output limit raised to 1200 tokens; OpenAI refusals and Gemini safety stops handled; cache and thinking tokens counted.
* Rate limits on rtac_lead and on new sessions, and the per-minute limiter fixed (it reset its window on every message).
* Flow editor: choices accept "pref", "send_to_ai" and "open"; link steps accept "booking": true; strict validation of step targets on save; two templates (question-first, name-first); JSON with quotes no longer corrupted on save.
* Widget: AI label in the header, clickable phone/email for show_contact, step-specific placeholders, keyboard type and autofill, 16px input on phones (no iOS zoom), no autofocus on touch, visible focus rings, AA contrast for muted text, reduced-motion covers the typing dots.

= 0.2.2 =
* Chats list and dashboard chart now include visitors who only answered the scripted flow. They were hidden behind a filter on AI messages, so a captured lead (name, phone, booking choice) was emailed out but missing from both — the most valuable sessions were the ones filtered away.
* Lead notifications accept several recipients: comma-separated, bare or "Name <address>". Previously the field held one address, and a second one would have failed validation and silently stopped every notification.
* New flow preference "question" for visitors who only want information: recorded on the session, never marked as a lead, no notification sent, and the AI is told not to push for contact details.
* The visitor-facing contact fallback shows only the first recipient, never the whole notification list.

= 0.2.1 =
* Plugin list: clearer name (Vestly.ai - AI Chatbot), homepage link to ristotoldsep.eu, Settings + Chats action links.

= 0.2.0 =
* Rating UI: a subtle 5-star bar appears after the visitor's second AI reply; ratings feed the dashboard average.
* Unanswered-questions report: the AI flags answers it had to give without knowledge-base coverage; new admin page lists the visitor's questions with one-click "Add to KB" prefill.
* Dashboard: 14-day conversations & leads chart, language split bar, unanswered-this-month card, rating count. Dependency-free inline SVG.
* Schema v2 (automatic migration, no reactivation needed).

= 0.1.8 =
* Booking button no longer offered for online-store products (courses, gift cards) — those get their product link in the reply instead.
* Repeat-guard: the booking button is not rendered again while a recent message already shows it.
* Linkifier keeps sentence punctuation out of URLs (broken trailing-dot links fixed).

= 0.1.7 =
* Rebranded as Vestly.ai (display name only — slug, prefixes and data unchanged). Powered-by links to ristotoldsep.eu.

= 0.1.6 =
* Booking button label is now translated (et/en/ru) — previously fell back to English "Book now" in every language — and is editable per language in the Texts tab.

= 0.1.5 =
* Chat history scrolls naturally on sites with smooth-scroll libraries (Lenis/Locomotive): data-lenis-prevent on the message list + overscroll containment.
* Prompt guardrail: when knowledge sources disagree on a price, only the official price list is quoted — no cross-source price ranges.

= 0.1.4 =
* Provider errors are now recorded in the chat transcript (visitor-facing message + internal cause, styled red in admin).
* Updated Gemini default models to Google's "-latest" aliases (dated 2.5 IDs were retired for new API users).

= 0.1.3 =
* Settings UX: API-key generation links per provider, model-per-tier inputs on one line, wider settings layout.

= 0.1.2 =
* Configurable launcher notification bubble (teaser): per-language text, delay, smart dismissal (7-day retry, never after chat opened).
* "Show chat widget" master toggle in settings.
* New Texts settings tab: per-language overrides for error message, over-cap message, input placeholder and "Select answer" placeholder.

= 0.1.1 =
* Widget CSS specificity fix (paddings/clipping) and premium visual refresh.

= 0.1.0 =
* Initial release.
